0 요약
이 기사는 소스 코드 레벨에서 SpringMVC의 프로세서 매핑 링크, 즉 컨트롤러를 찾는 세부 프로세스를 간략하게 설명합니다.
1 SpringMVC 요청 프로세스
컨트롤러는 위의 이미지에 해당하는 1 ~ 2 단계를 검색합니다.
SpringMVC 세부 작업 흐름도
2 SpringMVC 초기화 프로세스
2.1 먼저 두 가지 범주를 이해합니다
1. requestMappingInfo
캡슐화 요청 매핑 주석
HTTP 요청 헤더에 대한 관련 정보가 포함되어 있습니다
인스턴스는 요청 매핑 주석에 해당합니다
2. Handlermethod
캡슐화 컨트롤러에 대한 요청 처리 방법
메소드가 속한 Bean 객체, 메소드에 해당하는 메소드 개체, 메소드의 매개 변수 등이 포함됩니다.
요청 맵핑 상속 관계
SpringMVC 초기화 중
먼저 requestmappinghandlermpapping의 후 properTiesset을 실행합니다
그런 다음 APTRACTHANDLERMEDODMAPPING AFFPROPERTIESSET을 입력하십시오
이 메소드는이 클래스의 inithandlermethods에 들어갑니다
ApplicationContext에서 Bean을 스캔 한 다음 Bean에서 프로세서 메소드 찾기 및 등록을 담당합니다.
// ApplicationContext에서 Beans를 스캔하고, 핸들러를 감지하고 등록합니다. 보호 void inithandlermethods () {... // ApplicationContext string [] beannames = (this.detecthandLermeDsinanCestorContoxs.beannamesTyncludancestors (beannamestypontincludancestors)에서 모든 bean을 가져옵니다. getApplicationContext (). getBeannamesfortype (object.class)); // (String Beanname : Beannames)에 BeanName 배열을 전송합니다. {// ISHandler는 Bean 정의에 Bean을 기반으로 컨트롤러 주석이 포함되어 있는지 또는 요청 매핑 주석이 포함되어 있는지 판단합니다. if (ishandler (getApplicationContext (). getType (beanname))) {detecthandLermethods (beanname); }} Handlermethodsinitialized (gethandlermethods ());} requestMappingHandlerMpapping#ishandler
위의 방법은 현재 Bean 정의에 컨트롤러 주석이 포함되어 있는지 또는 요청 매핑 주석이 포함되어 있는지 확인하는 것입니다.
요청 매핑 만 발효되면? 아니요!
이 경우 스프링을 초기화 할 때 스프링 콩으로 등록되지 않으며 콩나무를 가로 질러 컨트롤러를 Compoent로 교체해도 괜찮지 만 일반적으로 수행되지는 않습니다.
Bean이 핸들러임을 확인한 후, 특정 처리기 방법은 Bean에서 찾을 수 있습니다 (즉, 컨트롤러 클래스에 정의 된 요청 처리 방법). 검색 코드는 다음과 같습니다
/** * 핸들러에서 핸들러 메소드를 찾으십시오 * @Param 핸들러 핸들러 또는 핸들러 인스턴스의 빈 이름 */보호 된 void detecthAndlerMethods (최종 개체 핸들러) {// 현재 컨트롤러 BeanClass의 클래스 개체 <?> handlerType = (handler instancef)? getApplicationContext (). getType ((String) handler) : handler.getClass (); // requestMappingInfo 인스턴스를 재구성하기 위해 getMappingFormEthod에 대한 반복 된 호출을 피하십시오. 최종 맵 <method, t> mappings = new IdentityHashMap <method, t> (); // 위와 동일하게, 그것은 또한 컨트롤러 bean 최종 클래스의 클래스 객체 <?> usertype = classutils.getUserClass (handlerType); // 현재 bean의 모든 핸들러 메소드를 가져옵니다. // 메소드에 따라 요청 매핑 //가있는 경우 requestMappingInfo 인스턴스 세트를 작성하십시오. handlerMethodSelector.selectMethods (userType, new MethodFilter () {@override public boolean matches (method apping) {t mapping formethod (hetmappormethod)); null) {mapping.put (method, mapping); // (메소드 메소드 : Method) {// handler 메소드를 등록하고 다음 메소드 레지스터 handlerMethod (handler, method, mappings.get (method)); } 위 코드에는 getMappingFormEthod를 호출하는 두 곳이 있습니다.
메소드 및 유형 레벨 요청 매핑 주석을 사용하여 requestMappingInfo 작성
@override protected requestmappingInfo getMappingFormEdod (메소드 메소드, 클래스 <?> handlerType) {requestMappingInfo info = null; // 메소드의 @RequestMapping RequestMapping MethodAntation = AnnotationUtils.FindAntation (메소드, requestMapping.class)을 가져옵니다. if (methodAntOntation! = null) {requestCondition <?> methodCondition = getCustommetHodCondition (method); Info = CreaterequestMappingInfo (MethodAntation, MethodCondition); // 메소드가 requestmapping typeannotation = AnnotationUtils.FindAntation (handlerType, requestMapping.class)의 @ReqUtestMapping 주석을 가져옵니다. if (typeannotation! = null) {requestCondition <?> typeCondition = getCustomTyPeconDition (handlerType); // 두 개의 @requestmapping 주석을 병합하십시오. info = createrequestmappingInfo (typeannotation, typeCondition) .combine (info); }} 리턴 정보; } 이 메소드의 목적은 핸들러 메소드 메소드를 기반으로 requestMappingInfo 객체를 작성하는 것입니다. 먼저, mehtod에 requestmpping 주석이 포함되어 있는지 확인하십시오. 그렇다면 주석의 내용을 기반으로 requestMappingInfo 객체를 직접 작성하십시오. 생성 후, 현재 방법에 요청 매핑 주석이 포함되어 있는지 여부를 결정하십시오. 이 주석이 포함되어 있으면 클래스의 주석에 따라 requestMappingInfo 객체가 생성됩니다. 그런 다음 병합 메소드의 requestMappingInfo 객체가 반환되고 병합 객체가 마침내 반환됩니다. 이제 DetecthandLermethods 메소드를 되돌아 보면 GetMappingFormEthod 메소드를 통한 두 가지 호출이 있습니다. 나는 개인적으로 이것이 최적화 될 수 있다고 생각합니다. 메소드가 처음에 처리기인지 판단 할 때, 생성 된 RequestMappingInfo 객체를 나중에 직접 저장하고 사용할 수 있으므로 requestMappingInfo 객체를 작성하는 프로세스가 누락되었음을 의미합니다. 그런 다음 다음과 같이 즉시 registerHandlerMehTod 메서드를 입력하십시오
보호 된 void registerHandlerMethod (객체 핸들러, 메소드 메소드, t 매핑) {// 핸들 레르 메드 핸드 레저드 생성 newHandlerMEDOD = createHandlerMethod (handler, method); Handlermethod OldHandlerMethod = Handlermethods.get (매핑); // (oldHandlerMethod! = null &&! OldHandlerMethod.equals (newHandlerMethod)) {New New ImplegalStateException ( "모호한 매핑을 찾을 수 없습니다." + NewHandlerMethod.getBean () + "Bean Method/NON"/nto + "/nto" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " +" + " oldhandlermethod.getBean () + " 'bean method/n" + OldHandlerMethod + "매핑"); } this.handlermethods.put (매핑, NewhandlerMethod); if (logger.isinfoenabled ()) {logger.info ( "mapped /" " + mapping +" /"에" + newhandlermethod); } // @RequestMapping 주석의 값을 얻은 다음 value-> requestMappingInfo Mapping Record를 URLMAP 세트 <string> 패턴에 추가합니다. = GetMappingPathPattern (매핑); for (문자열 패턴 : Patterns) {if (! getPathMatcher (). ispattern (pattern)) {this.urlmap.add (패턴, 매핑); }}} 여기서 t 유형은 requestMappingInfo입니다. 이 객체는 캡슐화 된 특정 컨트롤러에서 메소드의 요청 매핑 주석에 대한 관련 정보입니다. 요청 맵핑 주석은 requestMappingInfo 객체에 해당합니다. Handlermethod는 RequestMappingInfo와 유사하며 ControlELR에서 특정 처리 방법의 캡슐화입니다. 먼저 메소드의 첫 번째 줄을보고 핸들러 및 Mehthod를 기반으로 한 핸드머 메드 객체를 만듭니다. 두 번째 줄은 핸드 레저 메드 맵을 사용하여 전류 매핑에 해당하는 핸드머 메드를 가져옵니다. 그런 다음 동일한 요청 맵핑 구성이 존재하는지 확인하십시오. 다음 구성으로 인해 여기에 던져집니다
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
이상
@controller @requestmapping ( "/mambiguousTest") public class AmbiguousSestController {@requestMapping (value = "/test1") @ResponseBody public String test1 () {return "method test1"; } @RequestMapping (value = "/test1") @ResponseBody public String test2 () {return "method test2"; }} SpringMVC 시작 (초기화) 단계에서 요청 매핑 구성이 모호한 지 확인하십시오.이 단계는 모호성을 확인해야 할 사항 중 하나입니다 (런타임에서 모호성을 확인하는 장소는 나중에 언급됩니다). 그런 다음 구성이 정상임을 확인한 후에는 requestMappingInfo 및 handlermHethod 객체가 핸드머 메드 (LinkedHashMap)에 추가 된 다음 요청 매핑 주석화 된 값 및 reuQestMappingInfo 객체가 URLMAP에 추가됩니다.
레지스터 핸들러 메트 메서드의 간단한 요약
이 방법에는 세 가지 주요 책임이 있습니다
1. 요청 매핑 주석 구성이 모호한 지 확인하십시오.
2. handlermethod에 requestMappingInfo의 맵을 작성하십시오. 이 맵은 AbstracthandLermethodmapping의 멤버 변수 핸드 레저 메드입니다. LinkedHashMap.
3. AbstracthandLermethodmapping 및 Multivaluemap의 멤버 변수 URLMAP을 빌드하십시오. 이 데이터 구조는 맵>로 이해 될 수 있습니다. 문자열 유형 키는 처리 방법에 요청 매핑 주석의 값을 저장합니다. 특정 URI입니다
먼저 다음 컨트롤러가 있습니다
@controller @requestmapping ( "/urlmap") public class urlmapcontroller {@requestmapping (value = "/test1", method = returemethod.get) @responsebody public String test1 () {return "method test1"; } @RequestMapping (value = "/test1") @ResponseBody public String test2 () {return "method test2"; } @RequestMapping (value = "/test3") @ResponseBody public String test3 () {return "method test3"; }} 초기화가 완료되면 AbstracthandLermethodmapping에 해당하는 URLMAP의 구조는 다음과 같습니다.
위는 SpringMVC 초기화의 주요 프로세스입니다
검색 프로세스
질문으로 검색 프로세스를 이해하기 위해 다음 컨트롤러를 사용할 수 있습니다.
@controller @requestMapping ( "/lookuptest") public class lookuptestController {@requestMapping (value = "/test1", method = requestMethod.get) @ResponseBody public String test1 () {return "method test1"; } @requestmapping (value = "/test1", headers = "referer = https : //www.baidu.com") @ResponseBody public String test2 () {return "method test2"; } @requestMapping (value = "/test1", params = "id = 1") @ResponseBody public String test3 () {return "method test3"; } @RequestMapping (value = "/*") @ResponseBody public String test4 () {return "method test4"; }} 다음과 같은 요청이 있습니다
이 요청이 입력되는 방법은 무엇입니까?
요청을받은 후 웹 컨테이너 (Tomcat, Jetty)는 처리를 위해 Dispatcherservlet에 양도됩니다. FrameworkServlet은 해당 요청 메소드 (예 : Get lecl Doget)를 호출 한 다음 ProcessRequest 메소드를 호출합니다. ProcessRequest 방법을 입력 한 후 일련의 처리 후 Doservice 메소드를 줄을 입력하십시오 : 936. 그런 다음 Line856에 Dodispatch 메소드를 입력하십시오. 현재 요청 된 프로세서 핸들러를 라인으로 가져옵니다 : 896. 그런 다음 AbstracthandLermethodmapping의 LookupHandlerMethod 메소드를 입력하십시오. 코드는 다음과 같습니다
보호 된 핸드 레저드 LookupHandlerMethod (String LookupPath, httpservletRequest 요청) 예외 {list <catch> matches = new ArrayList <catch> (); // uri list <t> directPathMatches = this.urlmap.get (lookuppath)에 따라 DirectPathMatches를 가져옵니다. if (directPathMatches! = null) {addMatchingMappings (DirecTPathMatches, 일치, 요청); } // 직접 일치하는 requestmappingInfo가 없으며 모든 requestMappingInfo (matches.isempty ()) {// 선택 사항이 아니라 모든 매핑을 통과하는 것 외에는 AddMatchingMappings (this.handlerMethods.keyset (), 매치, 요청); } // 최상의 일치 요청 mappingInfo if (! matches.isempty ()) {비교기 <catch> 비교기 = New MatchComparator (getMappingComparator (request))에 해당하는 핸드머 메드를 가져옵니다. collections.sort (일치, 비교기); if (logger.istraceenabled ()) {logger.trace ( "found" + matches.size () + "[" + kookuppath + "]에 대한 매칭 매핑 :" + matches); } // 구성의 모호성이 다시 일치하는 가장 bestmatch = matches.get (0); if (matches.size ()> 1) {match SecondBestMatch = matches.get (1); if (comparator.compare (bestmatch, secondBestmatch) == 0) {method m1 = bestmatch.handlermethod.getMethod (); 메소드 m2 = secondBestMatch.handlerMethod.getMethod (); 새로운 불법 상태 상태를 던지십시오. }} handlematch (bestmatch.mapping, lookuppath, request); BestMatch.handlerMethod를 반환합니다. } else {return handlenomatch (handlermethods.keyset (), lookuppath, request); }} LookupPath에 따르면 LookUpPath = "/LookUptest/Test1", 즉 요청 된 URI에 따라 LookUpHandlerMethod 메서드를 입력하십시오. URLMAP를 직접 찾아 직접 일치하는 requestMappingInfo 목록을 가져옵니다. 여기서 우리는 3 requestMappingInfos와 일치합니다. 다음과 같이
그런 다음 AddMatchingMappings 메소드를 입력하십시오
개인 void addmatchingmappings (collection <t> 매핑, 목록 <catch> 매치, httpservletrequest 요청) {for (t 매핑 : 매핑) {t match = getMatchingMapping (매핑, 요청); if (match! = null) {matches.add (new Match (Match, Handlermethods.get (매핑))); }}} 이 방법의 책임은 현재 요청 된 URI와 Mappings의 RequestMappingInfo가 일치 할 수 있는지 여부를 가로 지르고 동일한 요청 mappingInfo 객체를 생성하는 것입니다. 그런 다음 requestMappingInfo에 해당하는 핸드 레저드를 가져옵니다. 그런 다음 매치 객체를 만들고 일치 목록에 추가하십시오. AddMatchingMappings 메소드를 실행 한 후 LookupHandlerMethod로 돌아갑니다. 현재 일치에는 일치 할 수있는 3 개의 requestMappingInfo 객체가 있습니다. 다음 프로세스는 매칭 목록을 정렬 한 다음 목록의 첫 번째 요소를 최상의 일치로 가져 오는 것입니다. 매치의 핸드머 메드를 반환합니다. 여기서 우리는 요청 mappinginfo의 비교 방법을 입력하고 특정 분류 로직을 살펴 봅니다. 코드는 다음과 같습니다
public int compareto (requestMappingInfo 기타, httpservletrequest 요청) {int result = patternscondition.compareto (기타 .getPatternScondition (), request); if (result! = 0) {return result; } result = paramscondition.compareto (기타. getParamScondition (), request); if (result! = 0) {return result; } result = headerscondition.compareto (기타 .getheaderscondition (), request); if (result! = 0) {return result; } result = considerCondition.comPareto (기타 .getConsumEscondition (), request); if (result! = 0) {return result; } result = produceScondition.compareto (기타 .getProduceScondition (), request); if (result! = 0) {return result; } result = methodscondition.compareto (기타 .getMethodScondition (), request); if (result! = 0) {return result; } result = customConditionHolder.comPareto (기타 .CustomConditionHolder, request); if (result! = 0) {return result; } 반환 0;} 코드에서 볼 수 있듯이 일치 순서는 값> 매개 변수> 헤더> 소비> 생성> 메소드> 사용자 정의입니다. 이것을 보면, 이전 질문에 쉽게 대답 할 수 있습니다. 동일한 값의 경우 매개 변수가 먼저 일치 할 수 있습니다. 따라서 해당 요청은 test3 () 메소드를 입력합니다. LookupHandlerMethod로 돌아가서 핸들 메모드를 찾으십시오. SpringMVC는 여기에서 구성의 모호성을 다시 확인합니다. 여기서 수표의 원칙은 두 개의 요청 mappinginfos를 가장 높은 일치 학위와 비교하는 것입니다. 여기서 SpringMVC를 초기화 할 때 모호한 구성 검사가 있는데 왜 여기에서 다시 확인됩니까? 현재 컨트롤러에 두 가지 메소드가있는 경우 초기화 모호성을 통해 다음 구성을 확인할 수 있습니다.
@requestmapping (value = "/test5", method = {returemethod.get, requestMethod.post})@responseBodyPublic String test5 () {return "method test5";}@requestMapping (value = "/test5", method = {returemethod.get, returemethod.delete})@replicepublic test6 () 이제 http : // localhost : 8080/springmvc-demo/lookuptest/test5 요청을 실행하면 LookupHandlerMethod 메서드에 던져집니다.
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5' 예외. 요청 methodsRequestCondition의 비교 방법이 비교 방법 번호이기 때문에 예외는 여기에 발생합니다. 코드는 다음과 같습니다
public int compareto (recomptsrequestCondition 기타, httpservletrequest request) {return other.methods.size () - this.methods.size ();}와일드 카드는 언제 일치합니까? URLMAP를 통해 값과 직접 일치하는 requestMappingInfo를 얻을 수없는 경우 와일드 카드 매칭을 사용하여 AddMatchingMappings 메소드를 입력합니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.