SpringBoot——MVC原理

更多内容,前往 IT-BLOG

一、SpringMVC自动配置


SpringMVC auto-configuration:SpringBoot 自动配置好了SpringMVC。以下是 SpringBoot 对 SpringMVC的默认配置:(WebMvcAutoConfiguration
【1】包括 ContentNegotiatingViewResolver 和 BeanNameViewResolver 如下:

 1 @Bean
 2 @ConditionalOnBean({ViewResolver.class})
 3 @ConditionalOnMissingBean(
 4     name = {"viewResolver"},
 5     value = {ContentNegotiatingViewResolver.class}
 6 )
 7 //存在于 WebMvcAutoConfiguration.java
 8 public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
 9     ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
10     resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
11     resolver.setOrder(-2147483648);
12     return resolver;
13 }
14 
15 //进入ContentNegotiatingViewResolver对象,查找解析视图的方法resolveViewName()
16 public View resolveViewName(String viewName, Locale locale) throws Exception {
17     RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
18     Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
19     List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
20     if(requestedMediaTypes != null) {
21         //获取候选的视图对象
22         List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
23         //选择最适合的视图对象
24         View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
25         if(bestView != null) {
26             return bestView;
27         }
28 }
29 
30 //进入上面的getCandidateViews()方法,查看获取的视图解析器,发现SpringBoot是将所有的视图解析器获取到viewResolvers,挨个遍历获取。
31 private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
32     List<View> candidateViews = new ArrayList();
33     Iterator var5 = this.viewResolvers.iterator();
34 
35 while(var5.hasNext()) {

【2】自动配置了 ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
【3】ContentNegotiatingViewResolver:组合所有的视图解析器的;

1 //进入ContentNegotiatingViewResolver发现初始化视图解析器的时候,是从容器中BeanFactoryUtils获取所有的视图解析器。
2 protected void initServletContext(ServletContext servletContext) {
3     Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
4         this.getApplicationContext(), ViewResolver.class).values();
5     if(this.viewResolvers == null) {

【4】如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;

 1 @Bean
 2  @ConditionalOnProperty(prefix = "spring.mvc", name = "date‐format")//在文件中配置日期格式化的规则
 3  public Formatter<Date> dateFormatter() {        
 4        return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件            
 5  } 
 6 
 7 //举个栗子如下:
 8 //可以自定义一个视图解析器,放入容器,springboot就会自动识别,继承viewreserve
 9 @Bean
10 public MyView myView(){
11     return new MyView();
12 }
13 //需要实现ViewResolver接口
14 private static class MyView implements ViewResolver{
15 
16     @Override
17     public View resolveViewName(String s, Locale locale) throws Exception {
18         return null;
19     }
20 }

【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调整。

1 @Bean
2 @ConditionalOnProperty(
3     prefix = "spring.mvc",
4     name = {"date-format"}
5 )
6 public Formatter<Date> dateFormatter() {
7     return new DateFormatter(this.mvcProperties.getDateFormat());
8 }

       ○ 自己添加的格式化器转换器,我们只需要放在容器中即可,上面代码块有演示。
【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。 继承抽象类既保留了所有的自动配置,也能用我们扩展的配置;

 1 //使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
 2 @Configuration
 3 public class MyMvcConfig extends WebMvcConfigurerAdapter {
 4     @Override
 5     public void addViewControllers(ViewControllerRegistry registry) {
 6        // super.addViewControllers(registry);
 7         //浏览器发送 /yintong 请求来到 success ,视图映射,当没有业务逻辑的时候就比较方便
 8         registry.addViewController("/yintong").setViewName("success");
 9     }
10 }

原理:
【1】WebMvcAutoConfiguration 是 SpringMVC的自动配置类;
【2】在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

 1 @Configuration
 2 public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {    
 3       private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
 4      //从容器中获取所有的WebMvcConfigurer    
 5       @Autowired(required = false)
 6       public void setConfigurers(List<WebMvcConfigurer> configurers) {
 7           if (!CollectionUtils.isEmpty(configurers)) {
 8               this.configurers.addWebMvcConfigurers(configurers);
 9              //一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;      
10              @Override    
11              public void addViewControllers(ViewControllerRegistry registry) {
12                  for (WebMvcConfigurer delegate : this.delegates) {
13                      delegate.addViewControllers(registry);
14                  }
15               }
16           }
17 }

【3】容器中所有的 WebMvcConfigurer都会一起起作用;
【4】我们的配置类也会被调用;
【5】效果:SpringMVC 的自动配置和我们的扩展配置都会起作用;

二、全面接管SpringMVC


让所有 SpringMVC的自动配置都失效。使用我们需要的配置,需要在配置类中添加 @EnableWebMvc即可。非常不推荐,不然使用 SpringBoot开发干嘛,哈哈。

 1 //使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能 
 2 @EnableWebMvc 
 3 @Configuration 
 4 public class MyMvcConfig extends WebMvcConfigurerAdapter {     
 5     @Override     
 6     public void addViewControllers(ViewControllerRegistry registry) {
 7         // super.addViewControllers(registry);         
 8         //浏览器发送 /atguigu 请求来到 success                 
 9         registry.addViewController("/atguigu").setViewName("success");     
10     } 
11 }

原理:为什么 @EnableWebMvc自动配置就失效了?
【1】@EnableWebMvc 的核心组合注解:

1 @Import(DelegatingWebMvcConfiguration.class)
2 public @interface EnableWebMvc {

【2】我们打开上面导入的 DelegatingWebMvcConfiguration 类,会发现其继承了 WebMvcConfigurationSupport。

1 @Configuration
2 public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

【3】我们看下 SpringBoot自动配置的文件,发现如下:@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),可知当容器中存在 WebMvcConfigurationSupport类时,就不会导入自动配置的类了,第二步导入的就是这个类。

 1 @Configuration
 2 @ConditionalOnWebApplication
 3 @ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
 4 WebMvcConfigurerAdapter.class })        
 5 //容器中没有这个组件的时候,这个自动配置类才生效
 6 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
 7 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
 8 @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
 9 ValidationAutoConfiguration.class })        
10 public class WebMvcAutoConfiguration {

【4】@EnableWebMvc 将 WebMvcConfigurationSupport 组件导入进来;
【5】导入的 WebMvcConfigurationSupport 只是 SpringMVC最基本的功能;
结论:在 SpringBoot 中会有非常多的 xxxConfigurer 帮助我们进行扩展配置。同时,在 SpringBoot 中也会有很多的xxxCustomizer 帮助我们进行定制配置。


 ----关注公众号,获取更多内容----

posted @ 2020-11-21 17:17  Java程序员进阶  阅读(61)  评论(0编辑  收藏  举报