SpringMVC原始碼解析系列3-HandleMapping

hahaeee發表於2018-03-20

介面定義

/**
 * Interface to be implemented by objects that define a mapping between
 * requests and handler objects.
 */
public interface HandlerMapping {
  //根據request獲取處理鏈
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

複製程式碼

介面實現

以RequestMappingHandlerMapping為例來講

RequestMappingHandlerMapping初始化過程

定義: 請求路徑-處理過程對映管理

打個比方就是根據你的http請求的路徑得到可以處理的handler(你的Controller方法)

先看下他的繼承關係

RequestMappingHandlerMapping

看到3個Spring的生命週期介面

  1. ServletContextAware:儲存ServletContext

    #org.springframework.web.context.support.WebApplicationObjectSupport
    @Override
    public final void setServletContext(ServletContext servletContext) {
    	if (servletContext != this.servletContext) {
    		//儲存ServletContext
    		this.servletContext = servletContext;
    		if (servletContext != null) {
    			//空實現
    			initServletContext(servletContext);
    		}
    	}
    }
    複製程式碼
  2. ApplicationContext:儲存Spring上下文

    @Override
    public final void setApplicationContext(ApplicationContext context) throws BeansException {
    ...
      //儲存SpringMVC上下文
      this.applicationContext = context;
      //儲存資源訪問器
      this.messageSourceAccessor = new MessageSourceAccessor(context);
      //空實現
      initApplicationContext(context);
    	...
    }
    複製程式碼
  3. InitlizingBean:初始化對映關係

    #org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
    //1.
    @Override
    public void afterPropertiesSet() {
       if (this.useRegisteredSuffixPatternMatch) {
          this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
       }
       super.afterPropertiesSet();
    }
    //4.
    @Override
    protected boolean isHandler(Class<?> beanType) {
    	return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
    			(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
    }
    //6.
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    	RequestMappingInfo info = null;
    	RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
    	if (methodAnnotation != null) {
    		//組裝對映資訊
    		RequestCondition<?> methodCondition = getCustomMethodCondition(method);
    		info = createRequestMappingInfo(methodAnnotation, methodCondition);
    		RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
    		if (typeAnnotation != null) {
    			RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
    			info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
    		}
    	}
    	return info;
    }
    #org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
    //2.
    @Override
    public void afterPropertiesSet() {
    	initHandlerMethods();
    }
    //3.
    protected void initHandlerMethods() {
    	if (logger.isDebugEnabled()) {
    		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    	}
    	//從容器中獲取所有object型別名
    	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
    			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
    			getApplicationContext().getBeanNamesForType(Object.class));
    	for (String beanName : beanNames) {
    		//抽象,過濾(在RequestMappingHandlerMapping中根據Controller和RequestMapping註解過濾)
    		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
    				isHandler(getApplicationContext().getType(beanName))){
    			//探測類中定義的handler方法
    			detectHandlerMethods(beanName);
    		}
    	}
    	handlerMethodsInitialized(getHandlerMethods());
    }
    //5.
    protected void detectHandlerMethods(final Object handler) {
    	Class<?> handlerType =
    			(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
    	final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
    	final Class<?> userType = ClassUtils.getUserClass(handlerType);
    	//得到符合條件的handler方法
    	Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
    		@Override
    		public boolean matches(Method method) {
    			//抽象,得到對映資訊(如RequestMappingInfo)
    			T mapping = getMappingForMethod(method, userType);
    			if (mapping != null) {
    				mappings.put(method, mapping);
    				return true;
    			}
    			else {
    				return false;
    			}
    		}
    	});
    	//註冊handler對映關係
    	for (Method method : methods) {
    		//儲存對映路徑和處理方法(還有跨域資訊)
    		registerHandlerMethod(handler, method, mappings.get(method));
    	}
    }
    複製程式碼

過程概括:

  1. 獲取所有object子類

  2. 根據條件過濾出handle處理類

  3. 解析handle類中定義的處理方法

  4. 註冊對映關係

    public void register(T mapping, Object handler, Method method) {
    			try {
      	//儲存handler和處理方法
    	HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    	...
         //儲存對映路徑和HandlerMethod
    	this.mappingLookup.put(mapping, handlerMethod);
    	//解析@CrossOrigin配置(跨域用)
    	CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      	//儲存跨域資訊
    	if (corsConfig != null) {
    		this.corsLookup.put(handlerMethod, corsConfig);
    	}
    	//儲存對映關係
    	this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
    }
    ...
    複製程式碼

}


## getHandler()實現

```java
#org.springframework.web.servlet.handler.AbstractHandlerMapping
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//抽象,呼叫子類實現得到一個handler(可以是任一物件,需要通過HandleAdapter來解析)
//RequestMappingInfoHandlerMapping中具體實現就是匹配請求路徑和RequestMapping註解
Object handler = getHandlerInternal(request);
...
//包裝handle成HandlerExecutionChain
 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
 //如果是跨域請求 則根據@CrossOrigin配置新增前置Intercept
 if (CorsUtils.isCorsRequest(request)) {
 	CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
 	CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
 	CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
 	executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
 }
 return executionChain;
}
#org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
@Override
 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 //得到對映路徑
 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
 ...
 try {
 	//根據對映路徑獲取HandlerMethod
 	HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
 	return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
 }
 ...
}
複製程式碼

相關文章