Spring MVC 학습 노트 의 Controller 찾기(Spring 4.0.3 기반)
16640 단어 springmvccontroller찾다
본 고 는 소스 코드 차원 에서 SpringMVC 의 프로세서 맵 절 차 를 간단하게 설명 하고 자 한다.즉,Controller 의 상세 한 과정 을 찾 는 것 이다.
1 SpringMVC 요청 절차
Controller 는 위의 그림 에서 대응 하 는 절차 1~2 의 과정 을 찾 습 니 다.
SpringMVC 상세 운행 흐름 도
2 SpringMVC 초기 화 과정
2.1 두 가지 유형 을 먼저 알 아야 한다.
1.RequestMappingInfo
패키지 RequestMapping 주석
HTTP 요청 헤더 에 대한 정보 포함
하나의 인 스 턴 스 가 RequestMapping 주석 에 대응 합 니 다.
2.HandlerMethod
패키지 컨트롤 러 의 처리 요청 방법
이 방법 에 속 하 는 bean 대상,이 방법 에 대응 하 는 method 대상,이 방법의 매개 변수 등 을 포함 합 니 다.
Request Mapping Handler Mapping 의 계승 관계
SpringMVC 초기 화 할 때
우선 Request Mapping Handler Mapping 의 after PropertiesSet 을 실행 합 니 다.
그리고 AbstractHandler MethodMapping 의 after PropertiesSet 에 들 어 갑 니 다.
이 방법 은 이러한 종류의 initHandler Methods 에 들 어 갑 니 다.
applicationContext 에서 beans 를 스 캔 한 다음 bean 에서 프로세서 방법 을 찾 아 등록 합 니 다.
//Scan beans in the ApplicationContext, detect and register handler methods.
protected void initHandlerMethods() {
...
// applicationContext bean name
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// beanName
for (String beanName : beanNames) {
//isHandler bean bean Controller RequestMapping
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
RequestMappingHandlerMapping#isHandler
위의 그림 방법 은 현재 bean 정의 에 Controller 주석 이나 RequestMapping 주석 이 있 는 지 판단 하 는 것 입 니 다.
RequestMapping 만 유효 합 니까?아니 야!
이러한 상황 에서 Spring 초기 화 할 때 이 종 류 를 Spring bean 으로 등록 하지 않 고,beanNames 를 옮 겨 다 닐 때 이 종 류 를 옮 겨 다 니 지 않 기 때문에 여기 서 Controller 를 Compoent 로 바 꿔 도 되 지만,일반적으로 이렇게 하지 않 습 니 다.
bean 을 handler 로 확정 하면 이 bean 에서 구체 적 인 handler 방법(즉,Controller 류 에서 구체 적 으로 정 의 된 요청 처리 방법)을 찾 습 니 다.코드 를 찾 으 면 다음 과 같 습 니 다.
/**
* Look for handler methods in a handler
* @param handler the bean name of a handler or a handler instance
*/
protected void detectHandlerMethods(final Object handler) {
// Controller bean class
Class<?> handlerType = (handler instanceof String) ?
getApplicationContext().getType((String) handler) : handler.getClass();
// getMappingForMethod RequestMappingInfo
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
// , Controller bean class
final Class<?> userType = ClassUtils.getUserClass(handlerType);
// bean handler method
// method RequestMapping
// RequestMappingInfo
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
// bean handler method
for (Method method : methods) {
// handler method,
registerHandlerMethod(handler, method, mappings.get(method));
}
상기 코드 는 두 곳 에서 getMappingForMethod 를 호출 하 였 습 니 다.RequestMapping 주 해 를 사용 하여 RequestMappingInfo 를 만 듭 니 다.
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
// method @RequestMapping
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
// method bean @RequtestMapping
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
// @RequestMapping
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
이 방법의 역할 은 handler method 방법 에 따라 Request MappingInfo 대상 을 만 드 는 것 이다.우선 이 mehtod 에 RequestMpping 주석 이 있 는 지 판단 합 니 다.있 으 면 이 주해 의 내용 에 따라 Request MappingInfo 대상 을 직접 만 듭 니 다.생 성 후 현재 method 에 속 한 bean 에 도 RequestMapping 주석 이 있 는 지 판단 합 니 다.이 주석 이 포함 되 어 있 으 면 이 클래스 의 주석 에 따라 RequestMappingInfo 대상 을 만 듭 니 다.그리고 method 에 있 는 RequestMappingInfo 대상 을 합 쳐 합 친 대상 을 되 돌려 줍 니 다.이제 와 서 detectHandlerMethods 방법 을 보면 getMappingForMethod 방법 이 두 군데 호출 되 었 습 니 다.개인 적 으로 여기 가 최적화 될 수 있다 고 생각 합 니 다.첫 번 째 로 method 가 handler 인지 아 닌 지 를 판단 할 때 만 든 RequestMappingInfo 대상 을 저장 할 수 있 습 니 다.직접 가 져 와 서 사용 하면 RequestMappingInfo 대상 을 만 드 는 과정 이 한 번 줄 어 듭 니 다.그리고 이어서 registerHandler Mehtod 에 들 어 가 는 방법 은 다음 과 같다.
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
// HandlerMethod
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
//
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
+ "' bean method
" + newHandlerMethod + "
to " + mapping + ": There is already '"
+ oldHandlerMethod.getBean() + "' bean method
" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
// @RequestMapping value, value->RequestMappingInfo urlMap
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}
여기 T 타 입 은 Request MappingInfo 입 니 다.이 대상 은 포 장 된 구체 적 인 컨트롤 러 아래 방법의 RequestMapping 주해 에 관 한 정보 입 니 다.RequestMapping 주 해 는 RequestMappingInfo 대상 에 대응 합 니 다.Handler Method 는 Request MappingInfo 와 유사 하 며,Controlelr 의 구체 적 인 처리 방법 에 대한 패키지 입 니 다.방법의 첫 줄 을 보고 handler 와 mehthod 에 따라 HandlerMethod 대상 을 만 듭 니 다.두 번 째 줄 은 handlerMethods map 를 통 해 현재 mapping 에 대응 하 는 HandlerMethod 를 가 져 옵 니 다.그리고 같은 RequestMapping 설정 이 있 는 지 판단 합 니 다.아래 와 같은 설정 은 이곳 에 던 질 수 있 습 니 다.Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
이상 하 다
@Controller
@RequestMapping("/AmbiguousTest")
public class AmbiguousTestController {
@RequestMapping(value = "/test1")
@ResponseBody
public String test1(){
return "method test1";
}
@RequestMapping(value = "/test1")
@ResponseBody
public String test2(){
return "method test2";
}
}
SpingMVC 시작(초기 화)단계 에서 RequestMapping 설정 에 잘못된 의미 가 있 는 지 확인 합 니 다.이것 은 잘못된 의 미 를 검사 하 는 곳 입 니 다.(다음 에 실행 할 때 잘못된 의 미 를 검사 하 는 곳 도 언급 됩 니 다)그리고 설정 이 정상 인 지 를 확인 한 후 이 Request MappingInfo 와 HandlerMethod 대상 을 handlerMethods(LinkedHashMap)에 추가 하고,이어서 Request Mapping 주해 의 value 와 Reuqest MappingInfo 대상 을 url Map 에 추가 합 니 다.registerHandlerMethod 방법 간단하게 요약
이 방법 은 주로 세 가지 직책 이 있다.
1.RequestMapping 주석 설정 에 잘못된 의미 가 있 는 지 확인 합 니 다.
2.RequestMappingInfo 를 HandlerMethod 에 매 핑 맵 을 구축 합 니 다.이 맵 은 AbstractHandler MethodMapping 의 구성원 변수 handlerMethods 입 니 다.LinkedHashMap。
3.AbstractHandler MethodMapping 의 구성원 변수 url Map,MultiValueMap 을 구축 합 니 다.이 데이터 구 조 는 그것 을 지도>로 이해 할 수 있다.그 중에서 String 형식의 key 는 처리 방법 에 RequestMapping 주해 의 value 를 저장 합 니 다.구체 적
먼저 다음 컨트롤 러 가 있 습 니 다.
@Controller
@RequestMapping("/UrlMap")
public class UrlMapController {
@RequestMapping(value = "/test1", method = RequestMethod.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";
}
}
초기 화 완료 후 AbstractHandler MethodMapping 에 대응 하 는 url Map 의 구 조 는 다음 과 같 습 니 다.이상 이 바로 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)가 요청 을 받 은 후 Dispatcher Servlet 에 맡 깁 니 다.Framework Servlet 에서 대응 하 는 요청 방법(eg:get 호출 doGet)을 호출 한 다음 processRequest 방법 을 호출 합 니 다.processRequest 방법 에 들 어간 후 일련의 처리 후 line:936 에서 doService 방법 에 들 어 갑 니 다.그리고 Line 856 에서 doDispatch 에 들 어 가 는 방법.line:896 에서 현재 요청 한 프로세서 handler 를 가 져 옵 니 다.그리고 AbstractHandler MethodMapping 의 lookup Handler Method 방법 에 들 어 갑 니 다.코드 는 다음 과 같다.
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// uri RequestMappingInfos
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// RequetMappingInfo, RequestMappingInfo
if (matches.isEmpty()) {
// No choice but to go through all mappings
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
// RequestMappingInfo HandlerMethod
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
//
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
lookupHandler Method 에 들 어 가 는 방법 입 니 다.그 중에서 lookupPath="/LookupTest/test 1"은 lookupPath 에 따라 요청 한 uri 입 니 다.url Map 을 직접 찾 아 직접 일치 하 는 Request MappingInfo list 를 가 져 옵 니 다.Request MappingInfo 3 개 와 일치 합 니 다.아래 와 같다그리고 addMatchingMappings 방법 에 들 어 갑 니 다.
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, handlerMethods.get(mapping)));
}
}
}
이 방법 은 현재 요청 한 uri 와 mappings 의 RequestMappingInfo 가 일치 할 수 있 는 지,일치 할 수 있다 면 같은 RequestMappingInfo 대상 을 만 드 는 것 입 니 다.RequestMappingInfo 에 대응 하 는 handlerMethod 를 가 져 옵 니 다.그리고 match 대상 을 만 들 고 matches list 에 추가 합 니 다.addMatchingMappings 방법 을 실행 하고 lookup Handler Method 로 돌아 갑 니 다.이때 matches 에는 Request MappingInfo 대상 과 일치 하 는 3 개가 있 습 니 다.다음 처 리 는 matchers 목록 을 정렬 한 다음 목록 의 첫 번 째 요 소 를 가 져 오 는 것 입 니 다.Match 의 HandlerMethod 를 되 돌려 줍 니 다.Request MappingInfo 의 compare To 방법 에 들 어가 서 구체 적 인 정렬 논 리 를 살 펴 보 겠 습 니 다.코드 는 다음 과 같다.
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result = patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
result = methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
코드 에서 알 수 있 듯 이 일치 하 는 우선 순 서 는 value>params>headers>consumes>produces>methods>custom 입 니 다.여기 서 앞의 문 제 를 보면 쉽게 답 을 얻 을 수 있 습 니 다.value 와 같은 상황 에서 params 가 먼저 일치 합 니 다.그래서 그 요청 은 test 3()방법 으로 들 어 갑 니 다.lookup Handler Method 로 돌아 가 Handler Method 를 찾 고 있 습 니 다.SpringMVC 는 또 이곳 에서 설정 의 유의 성 을 다시 한 번 검사 할 것 이다.여기 서 검사 하 는 원 리 는 일치 도가 가장 높 은 두 개의 RequestMappingInfo 를 비교 하 는 것 이다.SpringMVC 를 초기 화 하 는 데 설정 을 검사 하 는 오류 가 있 을 수 있 습 니 다.여기 서 왜 한 번 더 검 사 를 합 니까?현재 Controller 에 다음 과 같은 두 가지 방법 이 있다 면 다음 설정 은 잘못된 의 미 를 초기 화 할 수 있 습 니 다.
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String test5(){
return "method test5";
}
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.DELETE})
@ResponseBody
public String test6(){
return "method test6";
}
현재 실행http://localhost:8080/SpringMVC-demo/LookuptTest/test 5 요청 은 lookup Handler Method 방법 에서 던 집 니 다.이상이 이상 을 던 진 것 은 RequestMethods RequestCondition 의 compare To 방법 이 비교 methods 수 이기 때 문 입 니 다.코드 는 다음 과 같다.
public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {
return other.methods.size() - this.methods.size();
}
마스크 는 언제 일치 합 니까?url Map 을 통 해 value 와 직접 일치 하 는 RequestMappingInfo 를 얻 지 못 할 때 만 어댑터 가 addMatchingMappings 방법 에 들 어 갑 니 다.총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
springmvc application/octet-stream problemmistake: Source code: Solution: Summarize: application/octet-stream is the original binary stream method. If the convers...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.