優質博文:IT-BLOG-CN
一、SpringMVC自動配置
SpringMVC auto-configuration
:SpringBoot
自動配置好了SpringMVC
。以下是SpringBoot
對SpringMVC
的默認配置:[WebMvcAutoConfiguration
]
【1】包括ContentNegotiatingViewResolver
和BeanNameViewResolver
如下:
@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
:組合所有的視圖解析器的;
//進入ContentNegotiatingViewResolver發現初始化視圖解析器的時候,是從容器中BeanFactoryUtils獲取所有的視圖解析器。
protected void initServletContext(ServletContext servletContext) {Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.getApplicationContext(), ViewResolver.class).values();if(this.viewResolvers == null) {
【4】如何定制:我們可以自己給容器中添加一個視圖解析器;自動的將其組合進來;
@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{@Overridepublic 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
調整。
@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
類;
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}
它是從容器中獲取ConfigurableWebBindingInitializer
的,從而可知,我們可以配置一個ConfigurableWebBindingInitializer
來替換默認的(添加到容器),如果沒有配置會初始化一個Web
數據綁定器:
//初始化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
來進行擴展:
<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
。 繼承抽象類既保留了所有的自動配置,也能用我們擴展的配置;
//使用WebMvcConfigurerAdapter可以來擴展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {// super.addViewControllers(registry);//瀏覽器發送 /yintong 請求來到 success ,視圖映射,當沒有業務邏輯的時候就比較方便registry.addViewController("/yintong").setViewName("success");}
}
原理:
【1】WebMvcAutoConfiguration
是SpringMVC
的自動配置類;
【2】在做其他自動配置時會導入;@Import(EnableWebMvcConfiguration.class)
;
@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
開發干嘛,哈哈。
//使用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
的核心組合注解:
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
【2】我們打開上面導入的DelegatingWebMvcConfiguration
類,會發現其繼承了WebMvcConfigurationSupport
。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
【3】我們看下SpringBoot
自動配置的文件,發現如下:@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
,可知當容器中存在WebMvcConfigurationSupport
類時,就不會導入自動配置的類了,第二步導入的就是這個類。
@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
幫助我們進行定制配置。