<mvc:annotation-driven> 和 @EnableWebMvc 解析
这两个起到的作用是类似的(避免重复注册,二选一即可), 都是注册了大部分spring mvc开发所需的bean(HandlerMapping,HandlerAdapter等等),
还有根据包存在添加messageConverter(例如jackson,以支持@ResponseBody).
源码分析基于Spring 5.2.5, 为了简洁会省去部分源码.
前言
即使没有<mvc:annotation-driven>, DispatcherServlet一样会生成默认的映射器,适配器,视图解析器等.
public class DispatcherServlet extends FrameworkServlet {
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
protected void initStrategies(ApplicationContext context) {
// ...
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
}
}
// ...
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
}
从上述代码看出, DispatcherServlet会先从beanFactory中寻找所需要的bean, 若没找到, 则会加载默认配置(从jar包中的DispatcherServlet.properties读取)
由tomcat容器加载Servlet#init,最终执行该段代码
<mvc:annotation-driven>
从MvcNamespaceHandler
得出该标签由AnnotationDrivenBeanDefinitionParser
处理, 为了简化手动配置, 其中注册了很多bean(具体看源码).
该类还在静态代码块中判断jar包存在与否, 从而注册对应的HttpMessageConverter
, (比如,处理@RequestBody/ResponseBody的参数/返回值的消息转换器).
// 注意不要找错包
package org.springframework.web.servlet.config;
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
static {
// ...
ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}
public BeanDefinition parse(Element element, ParserContext context) {
// ... 在这里注册bean
}
}
@EnableWebMvc
该注解引入了@Import(DelegatingWebMvcConfiguration.class)
, 起到的作用与上面类似.
//----------------------------- 子类
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}
//----------------------------- 父类
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
@Bean
public HandlerMapping defaultServletHandlerMapping() {
DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
configureDefaultServletHandling(configurer);
return configurer.buildHandlerMapping();
}
//还有其他@Bean...
}
先看子类 DelegatingWebMvcConfiguration
, 从第7行看出, 子类主要做的是找到在spring注册的所有WebMvcConfigurer和重写方法(以便后续直接遍历调用).
再看父类WebMvcConfigurationSupport
, 该类同<mvc:annotation-driven>, 也是注册了很多bean(通过@Bean方式), 也存在类似的静态代码块判断jar包.
与<mvc:annotation-driven>不同的是, 在@Bean方法体内, 会调用WebMvcConfigurer(用户自定义)对应的方法, 以便于自定义配置这些bean.