优质博文:IT-BLOG-CN
一、SpringMVC自动配置
SpringMVC auto-configuration
:SpringBoot
自动配置好了SpringMVC
。以下是SpringBoot
对SpringMVC
的默认配置:[WebMvcAutoConfiguration
]
【1】包括ContentNegotiatingViewResolver
和BeanNameViewResolver
如下:
java
@Bean
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
name = {"viewResolver"},
value = {ContentNegotiatingViewResolver.class}
)
//存在于 WebMvcAutoConfiguration.java
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(-2147483648);
return resolver;
}
//进入ContentNegotiatingViewResolver对象,查找解析视图的方法resolveViewName()
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if(requestedMediaTypes != null) {
//获取候选的视图对象
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
//选择最适合的视图对象
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if(bestView != null) {
return bestView;
}
}
//进入上面的getCandidateViews()方法,查看获取的视图解析器,发现SpringBoot是将所有的视图解析器获取到viewResolvers,挨个遍历获取。
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
List<View> candidateViews = new ArrayList();
Iterator var5 = this.viewResolvers.iterator();
while(var5.hasNext()) {
【2】自动配置了ViewResolver
(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
【3】ContentNegotiatingViewResolver
:组合所有的视图解析器的;
JAVA
//进入ContentNegotiatingViewResolver发现初始化视图解析器的时候,是从容器中BeanFactoryUtils获取所有的视图解析器。
protected void initServletContext(ServletContext servletContext) {
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
this.getApplicationContext(), ViewResolver.class).values();
if(this.viewResolvers == null) {
【4】如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
java
@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date‐format")//在文件中配置日期格式化的规则
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
}
//举个栗子如下:
//可以自定义一个视图解析器,放入容器,springboot就会自动识别,继承viewreserve
@Bean
public MyView myView(){
return new MyView();
}
//需要实现ViewResolver接口
private static class MyView implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
【5】服务对静态资源的支持,静态资源文件夹路径,webjars
等。静态首页访问,自定义favicon.ico
图标文件的支持。
【6】自动注册了of Converter
,GenericConverter
,Formatter beans
;
○ Converter
:转换器; public String hello(User user)
:类型转换使用Converter
,String
转int
等等。
○ Formatter
格式化器; 2017.12.17===Date
,源码如下:可以看到格式可以通过spring.mvc.date-format
调整。
java
@Bean
@ConditionalOnProperty(
prefix = "spring.mvc",
name = {"date-format"}
)
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());
}
○ 自己添加的格式化器转换器,我们只需要放在容器中即可,上面代码块有演示。
【7】支持HttpMessageConverters
:
○ HttpMessageConverter
:SpringMVC
用来转换Http
请求和响应的;User
用Json
方式写出去;
○ HttpMessageConverters
是从容器中确定;获取所有的HttpMessageConverter
;
○ 自己给容器中添加HttpMessageConverter
,只需要将自己的组件注册容器中[@Bean
,@Component
]
【8】自动注册MessageCodesResolver
,定义错误代码生成规则。自动使用ConfigurableWebBindingInitializer
类;
java
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}
它是从容器中获取ConfigurableWebBindingInitializer
的,从而可知,我们可以配置一个ConfigurableWebBindingInitializer
来替换默认的(添加到容器),如果没有配置会初始化一个Web
数据绑定器:
java
//初始化Web数据绑定器,作用就是将请求数据绑定到JavaBean中,参数等,涉及数据转换等等
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(this.mvcConversionService());
initializer.setValidator(this.mvcValidator());
initializer.setMessageCodesResolver(this.getMessageCodesResolver());
return initializer;
}
【9】org.springframework.boot.autoconfigure.web
:web
的所有自动场景;上面能够得到的主要思想就是:如何修改Springboot
的默认配置:
1)、在自动配置很多组件的时候,先看容器中有木有用户自己配置的(@Bean,@Component)如果有就是用用户配置的,如果没有就是用自动配置的,因为底层使用了@ConditionalOnMiss注解来判断,容器中是否已经存在此类配置。
2)、如果有些组件可以配置多个,比如视图解析器(ViewResolver)将用户配置的和自己默认的组合起来。
扩展 SpringMVC: 官方解释:If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
【1】根据我们之前的配置xml
来进行扩展:
xml
<mvc:view‐controller path="/hello" view‐name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
【2】SpringBoot
编写一个配置类@Configuration
,继承WebMvcConfigurerAdapter
类型,不能标注@EnableWebMvc
。 继承抽象类既保留了所有的自动配置,也能用我们扩展的配置;
java
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /yintong 请求来到 success ,视图映射,当没有业务逻辑的时候就比较方便
registry.addViewController("/yintong").setViewName("success");
}
}
原理:
【1】WebMvcAutoConfiguration
是SpringMVC
的自动配置类;
【2】在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
;
java
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//从容器中获取所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
//一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;
@Override
public void addViewControllers(ViewControllerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addViewControllers(registry);
}
}
}
}
【3】容器中所有的WebMvcConfigurer
都会一起起作用;
【4】我们的配置类也会被调用;
【5】效果:SpringMVC
的自动配置和我们的扩展配置都会起作用;
二、全面接管SpringMVC
让所有SpringMVC
的自动配置都失效。使用我们需要的配置,需要在配置类中添加 @EnableWebMvc
即可。非常不推荐,不然使用SpringBoot
开发干嘛,哈哈。
java
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName("success");
}
}
原理: 为什么@EnableWebMvc
自动配置就失效了?
【1】@EnableWebMvc
的核心组合注解:
java
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
【2】我们打开上面导入的DelegatingWebMvcConfiguration
类,会发现其继承了WebMvcConfigurationSupport
。
java
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
【3】我们看下SpringBoot
自动配置的文件,发现如下:@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
,可知当容器中存在WebMvcConfigurationSupport
类时,就不会导入自动配置的类了,第二步导入的就是这个类。
java
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
//容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
【4】@EnableWebMvc
将WebMvcConfigurationSupport
组件导入进来;
【5】导入的WebMvcConfigurationSupport
只是SpringMVC
最基本的功能;
结论: 在SpringBoot
中会有非常多的xxxConfigurer
帮助我们进行扩展配置。同时,在SpringBoot
中也会有很多的xxxCustomizer
帮助我们进行定制配置。