本文研究背景:项目是传统SSM项目,非SpringBoot项目。项目使用xml方式配置SpringMVC并且没有手动往IOC容器中注入​​<mvc:annotation-driven/>​​注解注入的bean。

【1】BeanDefinitionParser

该接口被​​DefaultBeanDefinitionDocumentReader​​​使用以处理自定义和顶级(直属​​<beans/>​​)标签。

实现类可以自由地将标签里面的元数据转化成许多需要的​​BeanDefinition​​。

解析器从标签隶属的​​namespace​​​对应的​​NamespaceHandler​​​找到合适的​​BeanDefinitonParser​​。

该解析器的实现类有:

SpringMVC中<mvc:annotation-driven/>标签原理与实践详解_ico

我们看到有多个​​AnnotationDrivenBeanDefinitionParser​​​,他们是用来处理不同命名空间下的标签的,我们今天研究​​<annotation-driven/>​​​的是​​<mvc:annotation-driven/>​​​标签,所以我们找到对应的实现类​​org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser​​。

【2】AnnotationDrivenBeanDefinitionParser

通常如果我们希望通过注解的方式来进行Spring MVC开发,我们都会在​​***-servlet.xml​​​中加入​​<mvc:annotation-driven/>​​标签来告诉Spring我们的目的。

但是我们为什么这么做呢?这个标签是什么意思呢?它做了什么呢?

同样为了弄清楚这些问题, 像​​<context:component-scan/>​​​标签一样,我们先找到它的解析类。前面说过了,所有的自定义命名空间(像​​mvc,context​​​等)下的标签解析都是由​​BeanDefinitionParser​​接口的子类来完成的。

AnnotationDrivenBeanDefinitionParser的文档说明该解析器想容器中注入了如下类:

  • HandlerMapping处理器映射器
  • RequestMappingHandlerMapping,ordered at 0 for mapping requests to annotated controller methods.
  • BeanNameUrlHandlerMapping,ordered at 2 to map URL paths to controller bean names.
  • 也可以通过​​<mvc:view-controller>​​​或​​<mvc:resources>​​注册其他的HandlerMappings
  • HandlerAdapter处理器适配器
  • RequestMappingHandlerAdapter,for processing requests with annotated controller methods.
  • HttpRequestHandlerAdapter,使用HttpRequestHandler处理请求
  • SimpleControllerHandlerAdapter,for processing requests with interface-based {@link Controller Controllers}.
  • HandlerExceptionResolver
  • ExceptionHandlerExceptionResolver,通过@ExceptionHandler注解声明的方法处理异常
  • ResponseStatusExceptionResolver,处理那些标注了@ResponseStatus注解
  • DefaultHandlerExceptionResolver,解析已知的Spring异常类型

上面几个Bean实例。这几个类都是用来做什么的呢?

前两个是HandlerMapping接口的实现类,用来处理请求映射的。

其中第一个是处理@RequestMapping注解的。.
第二个会将controller类的名字映射为请求url。

中间三个是用来处理请求的。具体点说就是确定调用哪个controller的哪个方法来处理当前请求。

第一个处理@Controller注解的处理器,支持自定义方法参数和返回值(很酷)。
第二个是处理继承HttpRequestHandler的处理器。
第三个处理继承自Controller接口的处理器。

后面三个是用来处理异常的解析器,关于异常解析更多内容参考博文SpringMVC中异常处理

【3】代码追溯

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
Object source = context.extractSource(element);
XmlReaderContext readerContext = context.getReaderContext();

CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
context.pushContainingComponent(compDefinition);

RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);
// RequestMappingHandlerMapping
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

if (element.hasAttribute("enable-matrix-variables")) {
boolean enableMatrixVariables = Boolean.parseBoolean(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}

configurePathMatchingProperties(handlerMappingDef, element, context);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
// 跨域配置
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
//ConversionService
RuntimeBeanReference conversionService = getConversionService(element, source, context);
// Validator
RuntimeBeanReference validator = getValidator(element, source, context);
//MessageCodesResolver
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

//ConfigurableWebBindingInitializer
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

ManagedList<?> messageConverters = getMessageConverters(element, source, context);
ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);
String asyncTimeout = getAsyncTimeout(element);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
ManagedList<?> callableInterceptors = getInterceptors(element, source, context, "callable-interceptors");
ManagedList<?> deferredResultInterceptors = getInterceptors(element, source, context, "deferred-result-interceptors");

//RequestMappingHandlerAdapter
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);

if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}

handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);

//CompositeUriComponentsContributorFactoryBean
RootBeanDefinition uriContributorDef =
new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriContributorDef.setSource(source);
uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);

// ConversionServiceExposingInterceptor
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(source);
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);

// ExceptionHandlerExceptionResolver
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
methodExceptionResolver.setSource(source);
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
methodExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(methodExceptionResolver);
if (argumentResolvers != null) {
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);

// ResponseStatusExceptionResolver
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
statusExceptionResolver.setSource(source);
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
statusExceptionResolver.getPropertyValues().add("order", 1);
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);

// DefaultHandlerExceptionResolver
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);

context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
context.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));
// BeanNameUrlHandlerMapping
//HttpRequestHandlerAdapter
//SimpleControllerHandlerAdapter
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(context, source);

context.popAndRegisterContainingComponent();

return null;
}

注册默认mapping和adapter方法如下:

public static void registerDefaultComponents(ParserContext parserContext, Object source) {
registerBeanNameUrlHandlerMapping(parserContext, source);
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
}

【4】总结

我们知道了它们自动为我们注册了这么多的Bean,那这些Bean是做什么的呢?主要说明里面的两个,​​RequestMappingHandlerMapping和RequestMappingHandlerAdapter​​。

第一个是HandlerMapping的实现类,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
.
第二个是HandlerAdapter的实现类,它是处理请求的适配器,说白了,就是确定调用哪个类的哪个方法,并且构造方法参数,返回值。


那么它跟​​<context:component-scan/>​​有什么区别呢?

其实​​<context:component-scan/>​​​标签是告诉Spring 来扫描指定包下的类,并注册被​​@Component,@Controller,@Service,@Repository​​​等注解标记的组件(即,项目启动完成前这些被注解的组件就会被实例化bean放入容器上下文中)和注册​​AutowiredAnnotationBeanPostProcessor、 RequiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor以及 PersistenceAnnotationBeanPostProcessor​​。

而​​<mvc:annotation-driven/>​​​是告知Spring,我们启用注解驱动,为WEB 应用服务(我们就可以使用该标签注册的几个bean的功能)。然后Spring会自动为我们注册上面说到的几个Bean到工厂中,来处理我们的请求。换句话说,​​<context:component-scan/>​​​向容器中显式或隐式注册了一系列单个组件,但是项目要想将组件关联起来正常运转,则需要​​<mvc:annotation-driven/>​​注册的组件完成。

比如请求一个URL,我要知道这个URL匹配哪个controller中的哪个方法。哪个Controller就需要​​<context:component-scan/>​​​注解,如何匹配,匹配哪个方法就需要​​<mvc:annotation-driven/>​​注解。

当然,你也可以不使用​​<context:component-scan/>​​​而手动将所有需要的bean显示注册进applicationContext.xml中,这实在是太愚蠢!如果不想手动配置bean(xml),使用注解获取bean就必须使用​​<context:component-scan/>​​。

如果想使web项目正常运转,一般情况下,​​<mvc:annotation-driven/>​​和​​<context:component-scan/>​​是必需的!项目中二者相辅相成,缺一不可!

当然随着版本迭代、尤其是现在SpringBoot主流为我们省去了很多配置。但是想往高级走,我们还是需要了解其底层原理细节。

参考博文: context:annotation-config与mvc:annotation-driven和context:component-scan详解。