1. 程式人生 > >SpringFramework之@Controller/@RequestMapping解析

SpringFramework之@Controller/@RequestMapping解析

    分析版本Spring5.0.9.release,Springboot2.0.3.release

    spring-webmvc的META-INFO/spring.handles檔案中,有MvcNamespaceHandler,這是用來解析標籤的,來看下MvcNamespaceHandler的init(),如下List-1,我們暫時只關注AnnotationDrivenBeanDefinitionParser。

    List-1

@Override
public void init() {
    registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
    registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
    registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
    registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
    registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
    registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}

    AnnotationDrivenBeanDefinitionParser的parse方法裡面有很多程式碼,但是我們只關注倆個,如下List-2:

  1. 用RootBeanDefinition將RequestMappingHandlerMapping註冊到spring容器中。
  2. MvcNamespaceUtils.registerDefaultComponents中,將BeanNameUrlHandlerMapping註冊到spring容器中、將HttpRequestHandlerAdapter註冊到spring容器中、將SimpleControllerHandlerAdapter註冊到spring容器中、將HandlerMappingIntrospector註冊到容器中。

    List-2

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
    ...
    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    ...
    // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
    MvcNamespaceUtils.registerDefaultComponents(context, source);
    return null;
}

    RequestMappingHandlerMapping的繼承圖如下圖1所示:

                                                                          圖1

    圖1中,AbstractHandlerMethodMapping實現了介面InitializingBean,實現了afterPropertiesSet(),如果瞭解SpringIOC,應該知道這個方法意味著什麼,Spring在建立Bean的時候會呼叫這個方法。

    List-3

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

    由List-3知道afterPropertiesSet呼叫了initHandlerMethods(),如下List-4,首先從ApplicationContext或者所有的beanName,之後迴圈它們。isHandler方法由子類RequestMappingHandlerMapping實現,判斷類上是否有Controller或者RequestMapping註解,所以即使不加上@Controller,只是用@RequestMapping註解也可以。List-4中的handlerMethodsInitialized是空方法,不用管。

    List-4

protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));

	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			try {
				beanType = obtainApplicationContext().getType(beanName);
			}
			catch (Throwable ex) {
				// An unresolvable bean type, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
				}
			}
			if (beanType != null && isHandler(beanType)) {
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

    來看List-4中的方法detectHandlerMethods,傳入的是String型別的bean name,如下List-5,首先獲取bean name對應的Class,getMappingForMethod在RequestMappingHandlerMapping中實現,如List-6所示,判斷方法上是否有RequestMapping註解,如果有,則獲取引數、path、header等資訊,用builder模式構造出RequestMappingInfo。MethodIntrospector.selectMethods會遍歷類上的方法,所以整體上對類的所有方法進行是否有RequestMapping的檢查。需要注意的是,類似GetMapping等是組合註解,它們還是基於RequestMapping,所以方法上有GetMapping/PostMapping等都會會掃描並創建出對應的RequestMappingInfo。

    List-5

protected void detectHandlerMethods(Object handler) {
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

    List-6

@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
	}
	return info;
}

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

    List-5中的registerHandlerMethod,會將得到的Mapper資訊註冊到mappingRegistry到,這個mappingRegistry後面在DispatcherServlet中使用到。注意傳入到registerHandlerMethod方法的handler是String型別的bean name。

    Springboot中,RequestMappingHandlerMapping是通過@Configuration方式注入的,如下List-7

    List-7

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
...

Reference

  1. 原始碼
  2. https://www.cnblogs.com/lucas2/p/941