前言
spring boot 是基于spring 4 的基础上的一个框架,spring 4 有一个新特效–>基于java config 实现零配置.而在企业的实际工作中,spring 都是和spring mvc 紧密结合的,这篇文章从以下4个方面进行阐述:
- spring mvc 零配置
- spring mvc 零配置源码分析
spring mvc 零配置
项目结构图如下:
这里需要设定project Facets 中的 web 版本为 3.0 以上.如图:
这是因为spring mvc 零配置 是基于servlet 3.0 规范的.
在Servlet3.0规范,支持将web.xml相关配置也硬编码到代码中[servlet,filter,listener,等等],并由javax.servlet.ServletContainerInitializer的实现类负责在容器启动时进行加载.
spring提供了一个实现类org.springframework.web.SpringServletContainerInitializer,
该类会调用所有org.springframework.web.WebApplicationInitializer的实现类的onStartup(ServletContext servletContext)方法,将相关的组件注册到服务器.spring同时提供了一些WebApplicationInitializer的实现类供我们继承,以简化相关的配置,比如:org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer : 注册spring DispatcherServlet.
因此我们只需继承AbstractAnnotationConfigDispatcherServletInitializer 即可.
代码如下:
package com.demo.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{ // 指定spring application 的配置 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } // 指定spring web 配置 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } // 配置dispatcherServlet的映射路径 @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
RootConfig,用来指定扫描除去@Controller注解的类, 代码如下,
@Configuration @ComponentScan(basePackages = { "com.demo" }, excludeFilters={@Filter(type=FilterType.ANNOTATION,classes=Controller.class)}) public class RootConfig { }
其中,@Configuration 声明该类是一个配置类,@ComponentScan 声明了扫描包的范围,可配置多个, excludeFilters 用来去除扫描@Controller注解的类.
WebConfig用来配置Spring mvc 相关的配置,这里我们继承WebMvcConfigurerAdapter,用来简化配置.代码如下:
package com.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages = "com.demo.controller", useDefaultFilters = false, includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = { Controller.class }) }) public class WebConfig extends WebMvcConfigurerAdapter { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/jsp/function/"); viewResolver.setSuffix(".jsp"); return viewResolver; } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
其中@Configuration 声明该类是一个配置类,@EnableWebMvc 用来导入WebMvcConfigurationSupport中对于spring mvc的配置.@ComponentScan 用来指定扫描com.demo.controller包下被@Controller注解的类.
此外,还声明了对于jsp的ViewResolver,和覆盖了configureDefaultServletHandling来实例化DefaultServletHttpRequestHandler,该配置相当于在xml中配置的
<mvc:default-servlet-handler />
DefaultServletHttpRequestHandler的作用是它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理
- service 包的代码如下:
package com.demo.service; public interface TestService { String sayHello(); }
实现类:
package com.demo.service; import org.springframework.stereotype.Service; @Service public class TestServiceImpl implements TestService { @Override public String sayHello() { return "hi ,spring 4 "; } }
- controller代码:
package com.demo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.demo.service.TestService; @Controller public class TestController { @Autowired private TestService testService; @RequestMapping(value = "/test", method = RequestMethod.GET) @ResponseBody public String test() { return testService.sayHello(); } }
- pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jihegupiao.demo</groupId> <artifactId>spring4</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version> <project.compiler.version>1.8</project.compiler.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven-compiler-plugin.version}</version> <configuration> <source>${project.compiler.version}</source> <target>${project.compiler.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <url>http://localhost:8080/manager/text</url> <server>Tomcat7</server> <username>admin</username> <password>admin</password> <port>8082</port> <uriEncoding>UTF-8</uriEncoding> <path>/</path> <warFile>${basedir}/target/${project.build.finalName}.war</warFile> </configuration> </plugin> </plugins> </build> </project>
- 通过tomcat7:run 的方式启动,访问 测试链接 测试一下吧,如果正常的话,返回如下结果:
spring mvc 零配置源码分析
前言部分已经有提到,spring mvc 4 零配置是基于 servlet 3.0 规范的,在该规范中,是通过ServletContainerInitializer进行配置的,而在spring mvc中有一个唯一的实现–>SpringServletContainerInitializer,它就是打开宝箱的钥匙.代码如下:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
由于该类声明了@HandlesTypes(WebApplicationInitializer.class),则实现了Servlet 3.0 +规范的容器会依次扫描类路径下实现了WebApplicationInitializer接口的类传递到onStartup中.处理逻辑如下:
- 依次遍历webAppInitializerClasses,如果该类不是接口并且不是抽象类并且是WebApplicationInitializer的子类,则实例化后加入到initializers中.
如果initializers 为空集合,则打印一条日志后直接return.如下:
No Spring WebApplicationInitializer types detected on classpath
否则,排序后(如果它们存在@Order 注解,或者实现了Ordered 接口),依次调用其onStartup方法,进行启动.
WebApplicationInitializer 继承结构如下:
因此会最终调用AbstractDispatcherServletInitializer#onStartup方法,代码如下:public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); registerDispatcherServlet(servletContext); }
- 调用父类AbstractContextLoaderInitializer的onStartup以注册WebApplicationContext.
- 调用registerDispatcherServlet以注册DispatcherServlet.
AbstractContextLoaderInitializer#onStartup 代码如下:
@Override public void onStartup(ServletContext servletContext) throws ServletException { registerContextLoaderListener(servletContext); }
调用
protected void registerContextLoaderListener(ServletContext servletContext) { // 1. 创建WebApplicationContext 上下文 WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { // 2. 实例化ContextLoaderListener ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); // 3. 添加到ServletContext 中 servletContext.addListener(listener); } else { logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not return an application context"); } }
3件事:
调用抽象方法createRootApplicationContext来创建WebApplicationContext.该方法的实现在AbstractAnnotationConfigDispatcherServletInitializer中,代码如下:
protected WebApplicationContext createRootApplicationContext() { // 1. 获得RootConfigClasses Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { // 2. 初始化AnnotationConfigWebApplicationContext,并进行注册 AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); rootAppContext.register(configClasses); return rootAppContext; } else { return null; } }
- 调用抽象方法getRootConfigClasses,获得关于Spring的配置类.而对于我们当前来说,该方法的返回值就是demo工程中的RootConfig.class
- 如果getRootConfigClasses返回空数组,则返回null,否则进入第3步.
- 实例化AnnotationConfigWebApplicationContext,并进行注册.
- 如果创建失败,则打印日志,否则进入第3步.
- 实例化ContextLoaderListener,并添加到ServletContext中.
AbstractDispatcherServletInitializer#registerDispatcherServlet 代码如下:
protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return empty or null"); // 1. 初始化WebApplicationContext WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() did not return an application " + "context for servlet [" + servletName + "]"); // 2. 舒适化DispatcherServlet FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); // 3. 向servletContext 添加Servlet ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); Assert.notNull(registration, "Failed to register servlet with name '" + servletName + "'." + "Check if there is another servlet registered under the same name."); registration.setLoadOnStartup(1); registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported());// 默认支持异步 // 4. 获得Filter的配置,并依次进行注册 Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } // 5. 自定义初始化 customizeRegistration(registration); }
5件事:
调用createServletApplicationContext直接初始化WebApplicationContext,代码如下:
@Override protected WebApplicationContext createServletApplicationContext() { // 1. 初始化AnnotationConfigWebApplicationContext AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext(); // 2. 获得ServletConfigClasses Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { // 3. 向AnnotationConfigWebApplicationContext 注册 servletAppContext.register(configClasses); } return servletAppContext; }
- 实例化AnnotationConfigWebApplicationContext.
- 调用getServletConfigClasses 方法获得关于spring mvc的配置,如果不为空,则向AnnotationConfigWebApplicationContext进行注册,对于当前来说,是WebConfig.class.
- 舒适化DispatcherServlet,并设置ContextInitializers,一般来说,getServletApplicationContextInitializer方法返回的是null.
- 将dispatcherServlet 向servletContext进行注册.
- 获得Filter的配置,并依次进行注册
- 自定义初始化,默认是空实现
@EnableWebMvc源码分析
WebConfig声明了@EnableWebMvc,该注解通过@Import(DelegatingWebMvcConfiguration.class)的方式导入了DelegatingWebMvcConfiguration的配置,通过前几篇文章可以知道,此时spring 将加载DelegatingWebMvcConfiguration的配置. DelegatingWebMvcConfiguration类图如下:
通过查看源码可知,真正生效的配置是在WebMvcConfigurationSupport中,该类配置了如下几个bean:
RequestMappingHandlerMapping order 为0,用来处理被@controller注解的类的方法.代码如下:
@Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { // 1. 实例化RequestMappingHandlerMapping RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping(); handlerMapping.setOrder(0); // 2. 设置拦截器,默认注册的有ConversionServiceExposingInterceptor,ResourceUrlProviderExposingInterceptor handlerMapping.setInterceptors(getInterceptors()); handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager()); // 跨域设置 handlerMapping.setCorsConfigurations(getCorsConfigurations()); // 3. 实例化PathMatchConfigurer,该类是用来配置路径匹配的 PathMatchConfigurer configurer = getPathMatchConfigurer(); if (configurer.isUseSuffixPatternMatch() != null) { handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch()); } if (configurer.isUseRegisteredSuffixPatternMatch() != null) { handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch()); } if (configurer.isUseTrailingSlashMatch() != null) { handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch()); } UrlPathHelper pathHelper = configurer.getUrlPathHelper(); if (pathHelper != null) { handlerMapping.setUrlPathHelper(pathHelper); } PathMatcher pathMatcher = configurer.getPathMatcher(); if (pathMatcher != null) { handlerMapping.setPathMatcher(pathMatcher); } return handlerMapping; }
- 实例化RequestMappingHandlerMapping,设置order为0
- 设置拦截器,默认注册的有ConversionServiceExposingInterceptor,ResourceUrlProviderExposingInterceptor,可通过覆写addInterceptors来添加拦截器
- 跨域设置,默认没有配置,可通过覆写addCorsMappings进行添加跨域映射规则
- 获得PathMatchConfigurer,该类是用来配置路径匹配的,可通过configurePathMatch来个性化配置.
HandlerMapping,order 为1,用来处理URL path 直接映射到view 的名称,代码如下:
@Bean public HandlerMapping viewControllerHandlerMapping() { // 注册HandlerMapping ViewControllerRegistry registry = new ViewControllerRegistry(); registry.setApplicationContext(this.applicationContext); addViewControllers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); handlerMapping.setInterceptors(getInterceptors()); handlerMapping.setCorsConfigurations(getCorsConfigurations()); return handlerMapping; }
- 实例化ViewControllerRegistry
- 调用addViewControllers 进行配置.默认空实现,可以通过覆写的方式添加映射规则
- 对HandlerMapping 进行设置.
该类的具体使用可以参考如下链接:
BeanNameUrlHandlerMapping,order 为2,用来处理URL path 直接映射到controller的名字.代码如下:
@Bean public BeanNameUrlHandlerMapping beanNameHandlerMapping() { BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); mapping.setOrder(2); mapping.setInterceptors(getInterceptors()); mapping.setCorsConfigurations(getCorsConfigurations()); return mapping; }
HandlerMapping,order 为Integer.MAX_VALUE-1,用来处理静态资源.代码如下:
@Bean public HandlerMapping resourceHandlerMapping() { ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext, mvcContentNegotiationManager()); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { handlerMapping = new EmptyHandlerMapping(); } return handlerMapping; }
- 实例化ResourceHandlerRegistry
- 调用addResourceHandlers配置映射规则,默认空实现,可以通过覆写的方式添加映射规则
对HandlerMapping进行配置.
- 如果在addResourceHandlers中配置了映射规则,则会对其设置PathMatcher,UrlPathHelper,拦截器为ResourceUrlProviderExposingInterceptor
- 否则, HandlerMapping 为EmptyHandlerMapping 默认为EmptyHandlerMapping.
声明该bean,相当于在xml时代的配置:
<mvc:resources location="/html/" mapping="/html/**" />
HandlerMapping,order 为Integer.MAX_VALUE,用来处理转发请求到DispatcherServlet.代码如下:
@Bean public HandlerMapping defaultServletHandlerMapping() { DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); configureDefaultServletHandling(configurer); AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping(); handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); return handlerMapping; }
RequestMappingHandlerAdapter,处理请求通过被@controller注解的类的方法.
@Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); adapter.setContentNegotiationManager(mvcContentNegotiationManager()); adapter.setMessageConverters(getMessageConverters()); adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); adapter.setCustomArgumentResolvers(getArgumentResolvers()); adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { adapter.setRequestBodyAdvice( Collections.<RequestBodyAdvice>singletonList(new JsonViewRequestBodyAdvice())); adapter.setResponseBodyAdvice( Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice())); } AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); configureAsyncSupport(configurer); if (configurer.getTaskExecutor() != null) { adapter.setTaskExecutor(configurer.getTaskExecutor()); } if (configurer.getTimeout() != null) { adapter.setAsyncRequestTimeout(configurer.getTimeout()); } adapter.setCallableInterceptors(configurer.getCallableInterceptors()); adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); return adapter; }
- 实例化RequestMappingHandlerAdapter,可通过addArgumentResolvers来配置argument解析器,addReturnValueHandlers来配置返回值处理器,configureMessageConverters 可以配置消息转换器,如果该方法没有复写,则添加默认的
- 如果当前类路径存在com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator,则设置请求体拦截器为JsonViewRequestBodyAdvice,响应体拦截器为JsonViewResponseBodyAdvice
- 异步配置,可通过configureAsyncSupport进行配置
HttpRequestHandlerAdapter,处理请求通过HttpRequestHandler.调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。代码如下:
@Bean public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { return new HttpRequestHandlerAdapter(); }
SimpleControllerHandlerAdapter,处理请求通过Controller的实现类.代码如下:
@Bean public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { return new SimpleControllerHandlerAdapter(); }
HandlerExceptionResolverComposite.代码如下:
@Bean public HandlerExceptionResolver handlerExceptionResolver() { List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>(); configureHandlerExceptionResolvers(exceptionResolvers); if (exceptionResolvers.isEmpty()) { addDefaultHandlerExceptionResolvers(exceptionResolvers); } extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); composite.setExceptionResolvers(exceptionResolvers); return composite; }
- 通过调用configureHandlerExceptionResolvers进行个性化配置
- 如果exceptionResolvers为空,也就是意味着第1步没有进行注册,则调用addDefaultHandlerExceptionResolvers添加默认的ExceptionHandlerExceptionResolver–>ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver
- 实例化HandlerExceptionResolverComposite
- ExceptionHandlerExceptionResolver,处理通过被@ExceptionHandler注解的方法抛出的异常
- ResponseStatusExceptionResolver,用来处理被@ResponseStatus注解的所抛出的异常
- DefaultHandlerExceptionResolver,用来处理Spring异常体系抛出的异常
AntPathMatcher,代码如下:
@Bean public PathMatcher mvcPathMatcher() { // 注册PathMatcher PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher(); return (pathMatcher != null ? pathMatcher : new AntPathMatcher()); }
默认配置的是AntPathMatcher
UrlPathHelper,代码如下:
@Bean public UrlPathHelper mvcUrlPathHelper() { // 注册UrlPathHelper UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); return (pathHelper != null ? pathHelper : new UrlPathHelper()); }
默认配置的是UrlPathHelper
ContentNegotiationManager,该类的作用是根据请求规则决定返回什么样的内容类型。后缀规则、参数规则、Accept头规则、固定的内容类型等。注意,这里只是决定,不是具体提供内容类型的地方.代码如下:
@Bean public ContentNegotiationManager mvcContentNegotiationManager() { // 注册ContentNegotiationManager if (this.contentNegotiationManager == null) { ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext); configurer.mediaTypes(getDefaultMediaTypes()); configureContentNegotiation(configurer); try { this.contentNegotiationManager = configurer.getContentNegotiationManager(); } catch (Exception ex) { throw new BeanInitializationException("Could not create ContentNegotiationManager", ex); } } return this.contentNegotiationManager; }
lazy-init风格,会在实例化RequestMappingHandlerMapping时进行初始化.
- 实例化ContentNegotiationConfigurer
配置mediaTypes为:
- 如果当前类路径存在com.rometools.rome.feed.WireFeed,则注册atom–>application/atom+xml,rss–>application/rss+xml
- 如果当前类路径存在javax.xml.bind.Binder或者com.fasterxml.jackson.dataformat.xml.XmlMapper存在,则注册xml–>application/xml
- (如果当前类路径存在com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator)或者 (如果当前类路径存在com.google.gson.Gson),则注册json–>application/json
- 可通过覆写configureContentNegotiation进行个性化设置
- DefaultFormattingConversionService
- OptionalValidatorFactoryBean,如果是在JSR-303的环境,则进行注册
- HttpMessageConverter,依赖第3放类库
FormattingConversionService,用来格式化的.代码如下:
@Bean public FormattingConversionService mvcConversionService() { FormattingConversionService conversionService = new DefaultFormattingConversionService(); addFormatters(conversionService); return conversionService; }
Validator,用来mvc校验的.代码如下:
@Bean public Validator mvcValidator() { // 1. 该方法默认返回null,可以复写该方法 Validator validator = getValidator(); if (validator == null) { // 2. 如果存在javax.validation.Validator,则尝试加载org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) { Class<?> clazz; try { String className = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean"; clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new BeanInitializationException("Could not find default validator class", ex); } catch (LinkageError ex) { throw new BeanInitializationException("Could not load default validator class", ex); } validator = (Validator) BeanUtils.instantiateClass(clazz); } else { // 否则返回NoOpValidator validator = new NoOpValidator(); } } return validator; }
- 调用getValidator 获得Validator,该方法默认返回null,可以复写该方法
- 如果第1步返回null,如果存在javax.validation.Validator,则尝试加载org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean,否则返回NoOpValidator
CompositeUriComponentsContributor,MvcUriComponentsBuilder会用到,代码如下:
@Bean public CompositeUriComponentsContributor mvcUriComponentsContributor() { return new CompositeUriComponentsContributor( requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService()); }
ViewResolver,代码如下:
@Bean public ViewResolver mvcViewResolver() { ViewResolverRegistry registry = new ViewResolverRegistry(); registry.setContentNegotiationManager(mvcContentNegotiationManager()); registry.setApplicationContext(this.applicationContext); configureViewResolvers(registry); if (registry.getViewResolvers().isEmpty()) { String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.applicationContext, ViewResolver.class, true, false); if (names.length == 1) { registry.getViewResolvers().add(new InternalResourceViewResolver()); } } ViewResolverComposite composite = new ViewResolverComposite(); composite.setOrder(registry.getOrder()); composite.setViewResolvers(registry.getViewResolvers()); composite.setApplicationContext(this.applicationContext); composite.setServletContext(this.servletContext); return composite; }
- 实例化ViewResolverRegistry,并调用configureViewResolvers进行个性化配置
- 如果ViewResolverRegistry中ViewResolver为空,则默认添加InternalResourceViewResolver
- 初始化ViewResolverComposite
至此,spring mvc 零配置的源码就分析完了,关于这部分的流程图如下: