本文研究背景:项目是传统SSM项目,非SpringBoot项目。项目使用xml方式配置SpringMVC并且没有手动往IOC容器中注入mvc:annotation-driven
注解注入的bean。
通常如果我们希望通过注解的方式来进行Spring MVC开发,我们都会在***-servlet.xml
中加入
标签来告诉Spring我们的目的。
但是我们为什么这么做呢?这个标签是什么意思呢?它做了什么呢?
同样为了弄清楚这些问题, 像
标签一样,我们先找到它的解析类。前面说过了,所有的自定义命名空间(像mvc,context等)下的标签解析都是由BeanDefinitionParser接口的子类来完成的。
查看BeanDefinitionParser说明:
该接口被默认bean定义文档阅读者使用以处理自定义和顶级(直属
)标签。
实现者可以自由地将标签里面的元数据转化成许多需要的BeanDefinition。
解析器从标签隶属的namespace对应的NamespaceHandler找到合适的BeanDefinitonParser,
该解析器的实现类有:
我们看到有多个AnnotationDrivenBeanDefinitionParser,他们是用来处理不同命名空间下的标签的,我们今天研究
的是
标签,所以我们找到对应的实现类是org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser。
AnnotationDrivenBeanDefinitionParser的文档说明如下:
* A {@link BeanDefinitionParser} that provides the configuration for the
* {@code } MVC namespace element.
*
* This class registers the following {@link HandlerMapping}s:
*
* - {@link RequestMappingHandlerMapping}
* ordered at 0 for mapping requests to annotated controller methods.
*
- {@link BeanNameUrlHandlerMapping}
* ordered at 2 to map URL paths to controller bean names.
*
*
* Note: Additional HandlerMappings may be registered
* as a result of using the {@code } or the
* {@code } MVC namespace elements.
*
* This class registers the following {@link HandlerAdapter}s:
*
* - {@link RequestMappingHandlerAdapter}
* for processing requests with annotated controller methods.
*
- {@link HttpRequestHandlerAdapter}
* for processing requests with {@link HttpRequestHandler}s.
*
- {@link SimpleControllerHandlerAdapter}
* for processing requests with interface-based {@link Controller}s.
*
*
* This class registers the following {@link HandlerExceptionResolver}s:
*
* - {@link ExceptionHandlerExceptionResolver} for handling exceptions
* through @{@link ExceptionHandler} methods.
*
- {@link ResponseStatusExceptionResolver} for exceptions annotated
* with @{@link ResponseStatus}.
*
- {@link DefaultHandlerExceptionResolver} for resolving known Spring
* exception types
*
*
* This class registers an {@link org.springframework.util.AntPathMatcher}
* and a {@link org.springframework.web.util.UrlPathHelper} to be used by:
*
* - the {@link RequestMappingHandlerMapping},
*
- the {@link HandlerMapping} for ViewControllers
*
- and the {@link HandlerMapping} for serving resources
*
* Note that those beans can be configured by using the {@code path-matching} MVC namespace element.
*
* Both the {@link RequestMappingHandlerAdapter} and the
* {@link ExceptionHandlerExceptionResolver} are configured with instances of
* the following by default:
*
* - A {@link ContentNegotiationManager}
*
- A {@link DefaultFormattingConversionService}
*
- A {@link org.springframework.validation.beanvalidation.LocalValidatorFactoryBean}
* if a JSR-303 implementation is available on the classpath
*
- A range of {@link HttpMessageConverter}s depending on what 3rd party
* libraries are available on the classpath.
*
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Brian Clozel
* @author Agim Emruli
* @since 3.0
该注释文档说明该解析器想容器中注入了如下类:
RequestMappingHandlerMapping
BeanNameUrlHandlerMapping
RequestMappingHandlerAdapter
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
上面几个Bean实例。这几个类都是用来做什么的呢?
前两个是HandlerMapping接口的实现类,用来处理请求映射的。
其中第一个是处理@RequestMapping注解的。.
第二个会将controller类的名字映射为请求url。
中间三个是用来处理请求的。具体点说就是确定调用哪个controller的哪个方法来处理当前请求。
第一个处理@Controller注解的处理器,支持自定义方法参数和返回值(很酷)。
第二个是处理继承HttpRequestHandler的处理器。
第三个处理继承自Controller接口的处理器。
后面三个是用来处理异常的解析器。
源码如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
XmlReaderContext readerContext = parserContext.getReaderContext();
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
/* RequestMappingHandlerMapping order:0*/
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.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
else if (element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
RuntimeBeanReference validator = getValidator(element, source, parserContext);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
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, parserContext);
ManagedList> argumentResolvers = getArgumentResolvers(element, parserContext);
ManagedList> returnValueHandlers = getReturnValueHandlers(element, parserContext);
String asyncTimeout = getAsyncTimeout(element);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
ManagedList> callableInterceptors = getCallableInterceptors(element, source, parserContext);
ManagedList> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
/* 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);
}
else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
// "ignoreDefaultModelOnRedirect" spelling is deprecated
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
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);
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriCompContribDef.setSource(source);
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
readerContext.getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedCsInterceptorDef.setSource(source);
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedCsInterceptorDef);
/* ExceptionHandlerExceptionResolver */
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
exceptionHandlerExceptionResolver.setSource(source);
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(exceptionHandlerExceptionResolver);
if (argumentResolvers != null) {
exceptionHandlerExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
exceptionHandlerExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExceptionResolverName = readerContext.registerWithGeneratedName(exceptionHandlerExceptionResolver);
/* ResponseStatusExceptionResolver */
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
responseStatusExceptionResolver.setSource(source);
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
String responseStatusExceptionResolverName =
readerContext.registerWithGeneratedName(responseStatusExceptionResolver);
/* DefaultHandlerExceptionResolver */
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExceptionResolverName =
readerContext.registerWithGeneratedName(defaultExceptionResolver);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
/* BeanNameUrlHandlerMapping HttpRequestHandlerAdapter 和 SimpleControllerHandlerAdapter*/
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
parserContext.popAndRegisterContainingComponent();
return null;
}
注册默认mapping和adapter方法如下:
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
registerBeanNameUrlHandlerMapping(parserContext, source);
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
}
我们知道了它们自动为我们注册了这么多的Bean,那这些Bean是做什么的呢?
主要说明里面的两个,RequestMappingHandlerMapping和RequestMappingHandlerAdapter
。
第一个是HandlerMapping的实现类,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
.
第二个是HandlerAdapter的实现类,它是处理请求的适配器,说白了,就是确定调用哪个类的哪个方法,并且构造方法参数,返回值。
那么它跟
有什么区别呢?
其实
标签是告诉Spring 来扫描指定包下的类,并注册被@Component,@Controller,@Service,@Repository等注解标记的组件(即,项目启动完成前这些被注解的组件就会被实例化bean放入容器上下文中)和注册AutowiredAnnotationBeanPostProcessor、 RequiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor以及 PersistenceAnnotationBeanPostProcessor
。
而
是告知Spring,我们启用注解驱动,为WEB 应用服务(我们就可以使用该标签注册的几个bean的功能)。然后Spring会自动为我们注册上面说到的几个Bean到工厂中,来处理我们的请求。换句话说,
向容器中显式或隐式注册了一系列单个组件,但是项目要想将组件关联起来正常运转,则需要
注册的组件完成。
比如请求一个URL,我要知道这个URL匹配哪个controller中的哪个方法。哪个Controller就需要
注解,如何匹配,匹配哪个方法就需要
注解。
当然,你也可以不使用
而手动将所有需要的bean显示注册进applicationContext.xml中,这实在是太愚蠢!如果不想手动配置bean(xml),使用注解获取bean就必须使用
。
如果想使web项目正常运转,一般情况下,
和
是必需的!项目中二者相辅相成,缺一不可!!!
参考博文: context:annotation-config与mvc:annotation-driven和context:component-scan详解。