SpringWebMvc 自动装配大纲:
在引入 spring-boot-starter-web
的依赖后,SpringBoot 会自动进行Web环境的装载。
借由上一篇我们总结的规律,那么 SpringWebMvc 的自动配置就应该叫:WebMvcAutoConfiguration
1. WebMvcAutoConfiguration
复制 @ Configuration
//当前环境必须是WebMvc(Servlet)环境
@ ConditionalOnWebApplication (type = Type . SERVLET )
//当前运行环境的classpath中必须有Servlet类,DispatcherServlet类,WebMvcConfigurer类
@ ConditionalOnClass ({ Servlet . class , DispatcherServlet . class , WebMvcConfigurer . class })
//如果没有自定义WebMvc的配置类,则使用本自动配置
@ ConditionalOnMissingBean ( WebMvcConfigurationSupport . class )
@ AutoConfigureOrder ( Ordered . HIGHEST_PRECEDENCE + 10 )
@ AutoConfigureAfter ({ DispatcherServletAutoConfiguration . class , TaskExecutionAutoConfiguration . class ,
ValidationAutoConfiguration . class })
public class WebMvcAutoConfiguration
在配置中标注了,WebMvcAutoConfiguration
必须在 DispatcherServletAutoConfiguration
、TaskExecutionAutoConfiguration
、ValidationAutoConfiguration
执行完后再执行。故要先看他们都干了什么。
2. DispatcherServletAutoConfiguration
复制 @ AutoConfigureOrder ( Ordered . HIGHEST_PRECEDENCE )
@ Configuration
@ ConditionalOnWebApplication (type = Type . SERVLET )
@ ConditionalOnClass ( DispatcherServlet . class )
@ AutoConfigureAfter ( ServletWebServerFactoryAutoConfiguration . class )
public class DispatcherServletAutoConfiguration
文档注释原文翻译:
Auto-configuration for the Spring DispatcherServlet
. Should work for a standalone application where an embedded web server is already present and also for a deployable application using SpringBootServletInitializer
.
DispatcherServlet
的自动配置。它起作用应该依赖于一个已经存在嵌入式Web服务器的独立应用程序,也适用于使用 SpringBootServletInitializer
的可部署应用程序。
其中 SpringBootServletInitializer
是SpringBoot用于打war包时留给Web容器初始化应用的钩子。
至于它的作用,咱们暂且放在一边,留在WebMvc 部分详细来看。
DispatcherServletAutoConfiguration
的源码中又标注了 @AutoConfigureAfter
,说明它又要在 ServletWebServerFactoryAutoConfiguration
之后再执行。
3. ServletWebServerFactoryAutoConfiguration
复制 @ Configuration
//在自动配置中具有最高优先级执行
@ AutoConfigureOrder ( Ordered . HIGHEST_PRECEDENCE )
@ ConditionalOnClass ( ServletRequest . class )
@ ConditionalOnWebApplication (type = Type . SERVLET )
@ EnableConfigurationProperties ( ServerProperties . class )
@ Import ({ ServletWebServerFactoryAutoConfiguration . BeanPostProcessorsRegistrar . class ,
ServletWebServerFactoryConfiguration . EmbeddedTomcat . class ,
ServletWebServerFactoryConfiguration . EmbeddedJetty . class ,
ServletWebServerFactoryConfiguration . EmbeddedUndertow . class })
public class ServletWebServerFactoryAutoConfiguration
这个类又导入了几个组件:EmbeddedTomcat
、EmbeddedJetty
、EmbeddedUndertow
、BeanPostProcessorsRegistrar
。
3.1 EmbeddedTomcat
复制 @ Configuration
@ ConditionalOnClass ({ Servlet . class , Tomcat . class , UpgradeProtocol . class })
@ ConditionalOnMissingBean (value = ServletWebServerFactory . class , search = SearchStrategy . CURRENT )
public static class EmbeddedTomcat {
@ Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory () {
return new TomcatServletWebServerFactory() ;
}
}
由条件装配的注解 @ConditionalOnClass
可以看到,当前 classpath 下必须有 Tomcat
这个类,该配置类才会生效。对比 Jetty:
复制 import org . eclipse . jetty . server . Server ;
import org . eclipse . jetty . util . Loader ;
import org . eclipse . jetty . webapp . WebAppContext ;
@ Configuration
@ ConditionalOnClass ({ Servlet . class , Server . class , Loader . class , WebAppContext . class })
@ ConditionalOnMissingBean (value = ServletWebServerFactory . class , search = SearchStrategy . CURRENT )
public static class EmbeddedJetty
默认导入的 spring-boot-starter-web
中导入的是 Tomcat
的依赖,故 Jetty
不会生效。
3.2 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar
复制 public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar , BeanFactoryAware {
// ......
@ Override
public void registerBeanDefinitions ( AnnotationMetadata importingClassMetadata ,
BeanDefinitionRegistry registry) {
if ( this . beanFactory == null ) {
return ;
}
// 编程式注入组件
registerSyntheticBeanIfMissing(registry , "webServerFactoryCustomizerBeanPostProcessor" ,
WebServerFactoryCustomizerBeanPostProcessor . class ) ;
registerSyntheticBeanIfMissing(registry , "errorPageRegistrarBeanPostProcessor" ,
ErrorPageRegistrarBeanPostProcessor . class ) ;
}
private void registerSyntheticBeanIfMissing ( BeanDefinitionRegistry registry , String name , Class < ? > beanClass) {
if ( ObjectUtils . isEmpty ( this . beanFactory . getBeanNamesForType (beanClass , true , false ))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass) ;
beanDefinition . setSynthetic ( true );
registry . registerBeanDefinition (name , beanDefinition);
}
}
}
它实现了 ImportBeanDefinitionRegistrar
接口,在registerBeanDefinitions中可以编程式向IOC容器中注入组件。它注册的两个组件是:WebServerFactoryCustomizerBeanPostProcessor
、ErrorPageRegistrarBeanPostProcessor
。
3.2.1 WebServerFactoryCustomizerBeanPostProcessor
文档注释原文翻译:
BeanPostProcessor that applies all WebServerFactoryCustomizer beans from the bean factory to WebServerFactory beans.
Bean的后置处理器,它将 Bean 工厂中的所有 WebServerFactoryCustomizer 类型的 Bean 应用于 WebServerFactory 类型的 Bean。
可以看出它的作用是执行组件定制器 的,定制器下面会有介绍。
文档注释原文翻译:
BeanPostProcessor that applies all ErrorPageRegistrars from the bean factory to ErrorPageRegistry beans.
Bean的后置处理器,它将Bean工厂中的所有 ErrorPageRegistrars 应用于 ErrorPageRegistry 类型的Bean。
可推测出它的作用是将所有设置的错误页跳转规则注册到错误处理器中。
附: ErrorPageRegistry
接口的源码:
复制 public interface ErrorPageRegistry {
/**
* Adds error pages that will be used when handling exceptions.
* 添加错误页面
*/
void addErrorPages ( ErrorPage ... errorPages);
}
3.3 SpringBoot中的Customizer
一般情况下,修改 SpringBoot 的配置,都是通过 application.yml
显式地声明配置。除此之外,还可以使用 Customizer 定制器机制。
在WebMvc模块中,使用 Customizer 修改配置,可以实现 WebServerFactoryCustomizer
接口。该接口可以传入泛型,泛型的类型是 ServletWebServerFactory
。
以下是举例:
复制 @ Order ( 0 )
@ Component
public class WebMvcCustomizer implements WebServerFactoryCustomizer < TomcatServletWebServerFactory > , Ordered {
@ Override
public void customize ( TomcatServletWebServerFactory factory) {
factory . setPort ( 9090 );
factory . setContextPath ( "/demo" );
}
@ Override
public int getOrder () {
return 0 ;
}
}
Customizer 可以设置配置顺序(上面的 @Order
注解,或 Ordered
接口),通过配置执行顺序,可以自定义的覆盖某些自动配置,达到个性化配置的目的。
提这个 Customizer 机制,是为了看下面的配置类:
3.4 ServletWebServerFactoryAutoConfiguration中注册的其他组件
复制 public class ServletWebServerFactoryAutoConfiguration {
@ Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer ( ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties) ;
}
@ Bean
@ ConditionalOnClass (name = "org.apache.catalina.startup.Tomcat" )
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer (
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties) ;
}
巧了,它就创建了两个定制器,并把 ServerProperties
传入,让定制器根据配置信息做自动化配置。
3.4.1 ServerProperties的来源
复制 @ ConfigurationProperties (prefix = "server" , ignoreUnknownFields = true )
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
// ......
@ConfigurationProperties
的作用:可用于某个类上,设置属性profix用于指定在工程的全局配置文件(application.properties
或 application.yml
)中的配置的根信息。
简言之, @ConfigurationProperties
可以实现指定属性开头的属性值注入。
那么 ServerProperties
的属性值来源,就是全局配置文件中的server开头的所有配置。
以上执行完毕后,ServletWebServerFactoryAutoConfiguration
的全部配置也就完成了,下面执行 DispatcherServletAutoConfiguration
。
4. DispatcherServletAutoConfiguration
复制 // 最高配置优先级
@ AutoConfigureOrder ( Ordered . HIGHEST_PRECEDENCE )
@ Configuration
// Servlet环境下才生效
@ ConditionalOnWebApplication (type = Type . SERVLET )
@ ConditionalOnClass ( DispatcherServlet . class )
@ AutoConfigureAfter ( ServletWebServerFactoryAutoConfiguration . class )
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet" ;
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration" ;
// 注册DispatcherServlet的配置类
@ Configuration
@ Conditional ( DefaultDispatcherServletCondition . class )
@ ConditionalOnClass ( ServletRegistration . class )
// 启用配置文件与Properties的映射
@ EnableConfigurationProperties ({ HttpProperties . class , WebMvcProperties . class })
protected static class DispatcherServletConfiguration {
private final HttpProperties httpProperties;
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration ( HttpProperties httpProperties , WebMvcProperties webMvcProperties) {
this . httpProperties = httpProperties;
this . webMvcProperties = webMvcProperties;
}
// 构造DispatcherServlet
@ Bean (name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet () {
DispatcherServlet dispatcherServlet = new DispatcherServlet() ;
dispatcherServlet . setDispatchOptionsRequest ( this . webMvcProperties . isDispatchOptionsRequest ());
dispatcherServlet . setDispatchTraceRequest ( this . webMvcProperties . isDispatchTraceRequest ());
dispatcherServlet
. setThrowExceptionIfNoHandlerFound ( this . webMvcProperties . isThrowExceptionIfNoHandlerFound ());
dispatcherServlet . setEnableLoggingRequestDetails ( this . httpProperties . isLogRequestDetails ());
return dispatcherServlet;
}
// 注册文件上传组件
@ Bean
@ ConditionalOnBean ( MultipartResolver . class )
@ ConditionalOnMissingBean (name = DispatcherServlet . MULTIPART_RESOLVER_BEAN_NAME )
public MultipartResolver multipartResolver ( MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
// 注册DispatcherServletRegistration的配置类
@ Configuration
@ Conditional ( DispatcherServletRegistrationCondition . class )
@ ConditionalOnClass ( ServletRegistration . class )
@ EnableConfigurationProperties ( WebMvcProperties . class )
@ Import ( DispatcherServletConfiguration . class )
protected static class DispatcherServletRegistrationConfiguration {
private final WebMvcProperties webMvcProperties;
private final MultipartConfigElement multipartConfig;
public DispatcherServletRegistrationConfiguration ( WebMvcProperties webMvcProperties ,
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
this.webMvcProperties = webMvcProperties;
this.multipartConfig = multipartConfigProvider.getIfAvailable();
}
// 辅助注册DispatcherServlet的RegistrationBean
@ Bean (name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ ConditionalOnBean (value = DispatcherServlet . class , name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet ,
this.webMvcProperties.getServlet().getPath());
registration . setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration . setLoadOnStartup( this . webMvcProperties . getServlet() . getLoadOnStartup());
if ( this . multipartConfig != null ) {
registration . setMultipartConfig( this . multipartConfig );
}
return registration;
}
}
//......
里面嵌套了两个内部类,分别注册 DispatcherServlet
和 DispatcherServletRegistrationBean
。
在继续阅读注册 DispatcherServlet
之前,先了解 SpringBoot 注册Servlet的机制。
4.1 SpringBoot注册传统Servlet三大组件
由于 SpringBoot 项目中没有 web.xml(Servlet3.0规范中就没有了),故有另外的方式注册Servlet三大组件。SpringBoot 提供两种方式。
4.1.1 组件扫描@ServletComponentScan
在启动类上标注 @ServletComponentScan
注解,指定 value/basePackage
,即可扫描指定包及子包下所有的 Servlet 组件。
之后注册 Servlet
、Filter
、Listener
组件,就可以像 Servlet3.0 规范后的方式,直接在 Servlet 上标注 @WebServlet
等注解即可。
4.1.2 借助RegistrationBean
自定义的Servlet可以创建 ServletRegistrationBean<T extends Servlet>
。
使用时,只需要在配置类中注册一个 ServletRegistrationBean
,创建它的对象时,使用有参构造方法,传入 Servlet 和 urlMapping 即可。
一个简单的例子:
复制 public class DemoServlet extends HttpServlet {
@ Override
protected void doGet ( HttpServletRequest req , HttpServletResponse resp) throws ServletException , IOException {
resp . getWriter () . println ( "demo servlet" );
}
}
public class DemoServletRegistryBean extends ServletRegistrationBean < DemoServlet > {
public DemoServletRegistryBean ( DemoServlet servlet , String ... urlMappings) {
super(servlet , urlMappings);
}
}
@ Configuration
public class ServletConfiguration {
@ Bean
public DemoServletRegistryBean demoServletRegistryBean () {
return new DemoServletRegistryBean( new DemoServlet() , "/demo/servlet" ) ;
}
}
4.2 注册DispatcherServlet
只关注核心部分源码:
复制 @ EnableConfigurationProperties ({ HttpProperties . class , WebMvcProperties . class })
protected static class DispatcherServletConfiguration {
private final HttpProperties httpProperties;
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration ( HttpProperties httpProperties , WebMvcProperties webMvcProperties) {
this . httpProperties = httpProperties;
this . webMvcProperties = webMvcProperties;
}
@ Bean (name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet () {
DispatcherServlet dispatcherServlet = new DispatcherServlet() ;
dispatcherServlet . setDispatchOptionsRequest ( this . webMvcProperties . isDispatchOptionsRequest ());
dispatcherServlet . setDispatchTraceRequest ( this . webMvcProperties . isDispatchTraceRequest ());
dispatcherServlet
. setThrowExceptionIfNoHandlerFound ( this . webMvcProperties . isThrowExceptionIfNoHandlerFound ());
dispatcherServlet . setEnableLoggingRequestDetails ( this . httpProperties . isLogRequestDetails ());
return dispatcherServlet;
}
DispatcherServletConfiguration
类上标注了 @EnableConfigurationProperties
,代表启用指定类的 ConfigurationProperties
功能。
下面创建 DispatcherServlet
,并将默认的一些配置设置到 DispatcherServlet
中。这些属性就来自于已经被启用的 HttpProperties
、WebMvcProperties
中。
至此,DispatcherServletAutoConfiguration
执行完毕,回到 WebMvcAutoConfiguration
中。
5. WebMvcAutoConfiguration
5.1 WebMvcConfiguration
复制 @ Configuration
// 导入配置类
@ Import ( EnableWebMvcConfiguration . class )
// 启用WebMvcProperties、ResourceProperties
@ EnableConfigurationProperties ({ WebMvcProperties . class , ResourceProperties . class })
@ Order ( 0 )
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer , ResourceLoaderAware
在SpringBoot2.x中,自定义的WebMvc配置需要实现 WebMvcConfigurer
接口,并重写接口中需要配置的方法即可。
WebMvcAutoConfigurationAdapter
也实现了该接口,并进行默认配置。
5.1.1 配置HttpMessageConverter
复制 @ Override
public void configureMessageConverters( List<HttpMessageConverter<?>> converters) {
this . messageConvertersProvider
. ifAvailable ((customConverters) -> converters . addAll ( customConverters . getConverters ()));
}
5.1.2 ViewResolver的组件注册
复制 // 最常用的视图解析器
@ Bean
@ ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver() ;
// 使用前后缀拼接的方式
resolver . setPrefix ( this . mvcProperties . getView () . getPrefix ());
resolver . setSuffix ( this . mvcProperties . getView () . getSuffix ());
return resolver;
}
@ Bean
@ ConditionalOnBean ( View . class )
@ ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver() ;
resolver . setOrder ( Ordered . LOWEST_PRECEDENCE - 10 );
return resolver;
}
@ Bean
@ ConditionalOnBean ( ViewResolver . class )
@ ConditionalOnMissingBean (name = "viewResolver" , value = ContentNegotiatingViewResolver . class )
public ContentNegotiatingViewResolver viewResolver( BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver() ;
resolver . setContentNegotiationManager ( beanFactory . getBean ( ContentNegotiationManager . class ));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver . setOrder ( Ordered . HIGHEST_PRECEDENCE );
return resolver;
}
// 国际化组件
@ Bean
@ ConditionalOnMissingBean
@ ConditionalOnProperty (prefix = "spring.mvc" , name = "locale" )
public LocaleResolver localeResolver() {
if ( this . mvcProperties . getLocaleResolver () == WebMvcProperties . LocaleResolver . FIXED ) {
return new FixedLocaleResolver( this . mvcProperties . getLocale()) ;
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver() ;
localeResolver . setDefaultLocale ( this . mvcProperties . getLocale ());
return localeResolver;
}
注册了ViewResolver
、LocaleResolver
。
ContentNegotiatingViewResolver
:最高级的 ViewResolver,负责将视图解析的工作代理给不同的 ViewResolver 来处理不同的View
BeanNameViewResolver
:如果 Controller 中返回的视图名称恰好有一个Bean的名称与之相同,则会交予Bean处理
InternalResourceViewResolver
:最常用的 ViewResolver,通过设置前后缀来匹配视图
5.1.3 静态资源映射
复制 @ Override
public void addResourceHandlers( ResourceHandlerRegistry registry) {
if ( ! this . resourceProperties . isAddMappings ()) {
logger . debug ( "Default resource handling disabled" );
return ;
}
Duration cachePeriod = this . resourceProperties . getCache () . getPeriod ();
CacheControl cacheControl = this . resourceProperties . getCache () . getCachecontrol () . toHttpCacheControl ();
// 映射webjars
if ( ! registry . hasMappingForPattern ( "/webjars/**" )) {
customizeResourceHandlerRegistration( registry . addResourceHandler( "/webjars/**" )
. addResourceLocations( "classpath:/META-INF/resources/webjars/" )
. setCachePeriod(getSeconds(cachePeriod)) . setCacheControl(cacheControl)) ;
}
// 映射静态资源路径
String staticPathPattern = this . mvcProperties . getStaticPathPattern ();
if ( ! registry . hasMappingForPattern (staticPathPattern)) {
customizeResourceHandlerRegistration( registry . addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
. setCachePeriod(getSeconds(cachePeriod)) . setCacheControl(cacheControl)) ;
}
}
注册静态资源路径。可以看到,它将 /webjars
路径下的资源都映射到 classpath:/META-INF/resources/webjars
中。
webjars 可以将前端的框架变成Maven依赖,减少手动加入静态资源的工作。
除了注册 webjars 的资源路径,倒数第二行,还取到 resourceProperties
中的 staticLocations
,也加入进去。
而 ResourceProperties
中的 staticLocations:
复制 private static final String [] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/" ,
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
private String [] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
这也解释了为什么静态资源文件放在 resources 中和放在 static 中都能被正常加载的原因。
5.1.4 主页的设置
复制 @ Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping( ApplicationContext applicationContext) {
// 调用getWelcomePage,跳转到下面的方法中
return new WelcomePageHandlerMapping( new TemplateAvailabilityProviders(applicationContext) ,
applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
static String [] getResourceLocations( String [] staticLocations) {
String [] locations = new String [ staticLocations . length + SERVLET_LOCATIONS . length ];
System . arraycopy(staticLocations , 0 , locations , 0 , staticLocations . length );
System . arraycopy(SERVLET_LOCATIONS , 0 , locations , staticLocations . length , SERVLET_LOCATIONS . length );
return locations;
}
private Optional< Resource > getWelcomePage() {
String [] locations = getResourceLocations( this . resourceProperties . getStaticLocations());
// this::getIndexHtml调用下面的方法
return Arrays . stream(locations) . map( this :: getIndexHtml) . filter( this :: isReadable) . findFirst();
}
private Resource getIndexHtml( String location) {
return this . resourceLoader . getResource(location + "index.html" );
}
由此可以看出,欢迎页面/主页的设置,是取的静态资源路径中的 **index.html**
文件 。
5.1.5 应用图标的设置
复制 @ Configuration
@ ConditionalOnProperty (value = "spring.mvc.favicon.enabled" , matchIfMissing = true )
public static class FaviconConfiguration implements ResourceLoaderAware {
// ......
// 配置图标映射器
@ Bean
public SimpleUrlHandlerMapping faviconHandlerMapping () {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping() ;
mapping . setOrder ( Ordered . HIGHEST_PRECEDENCE + 1 );
mapping . setUrlMap ( Collections . singletonMap ( "**/favicon.ico" , faviconRequestHandler() ));
return mapping;
}
// .......
}
可以明显的看到默认的图标名称是 favicon.ico
,且放在静态路径下的任意位置都可以被扫描到。
5.2 EnableWebMvcConfiguration
5.2.1 注册的核心组件
复制 // 处理器适配器
@ Bean
@ Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super . requestMappingHandlerAdapter ();
adapter . setIgnoreDefaultModelOnRedirect (
this . mvcProperties == null || this . mvcProperties . isIgnoreDefaultModelOnRedirect ());
return adapter;
}
// 处理器映射器
@ Bean
@ Primary
@ Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super . requestMappingHandlerMapping ();
}
SpringWebMvc 中最核心的两个组件:处理器适配器、处理器映射器。
复制 // 校验器
@ Bean
@ Override
public Validator mvcValidator() {
if ( ! ClassUtils . isPresent ( "javax.validation.Validator" , getClass() . getClassLoader ())) {
return super . mvcValidator ();
}
return ValidatorAdapter . get ( getApplicationContext() , getValidator() );
}
注册了 Hibernate-Validator 参数校验器。
复制 @ Override
// 全局异常处理器
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
if ( this . mvcRegistrations != null && this . mvcRegistrations . getExceptionHandlerExceptionResolver () != null ) {
return this . mvcRegistrations . getExceptionHandlerExceptionResolver ();
}
return super . createExceptionHandlerExceptionResolver ();
}
@ Override
protected void configureHandlerExceptionResolvers( List< HandlerExceptionResolver > exceptionResolvers) {
super . configureHandlerExceptionResolvers (exceptionResolvers);
if ( exceptionResolvers . isEmpty ()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers) ;
}
if ( this . mvcProperties . isLogResolvedException ()) {
for ( HandlerExceptionResolver resolver : exceptionResolvers) {
if (resolver instanceof AbstractHandlerExceptionResolver) {
((AbstractHandlerExceptionResolver) resolver) . setWarnLogCategory ( resolver . getClass () . getName ());
}
}
}
}
注册了全局异常处理器。
小结
自动配置类是有执行顺序的, WebMvcAutoConfiguration
的执行顺序在 ServletWebServerFactoryAutoConfiguration
、DispatcherServletAutoConfiguration
之后。
SpringBoot会根据当前classpath下的类来决定装配哪些组件,启动哪种类型的Web容器。
WebMvc的配置包括消息转换器、视图解析器、处理器映射器、处理器适配器、静态资源映射配置、主页设置、应用图标设置等。
配置 SpringBoot 应用除了可以使用 properties、yml 之外,还可以使用 Customizer 来编程式配置。