• 9 Web开发——springmvc自动配置原理


    官方文档目录:

    https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-spring-mvc

     1. Spring MVC auto-configuration

      Spring Boot 自动配置好了SpringMVC,以下是SpringBoot对SpringMVC的默认配置:

    WebMvcAutoConfiguration)

      - Inclusion of `ContentNegotiatingViewResolver` and `BeanNameViewResolver` beans.
      - 自动配置了ViewResolver视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))

      • public class WebMvcAutoConfiguration {
                @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(2147483637);
                    return resolver;
                }
        
                @Bean
                @ConditionalOnBean({ViewResolver.class})
                @ConditionalOnMissingBean(
                    name = {"viewResolver"},
                    value = {ContentNegotiatingViewResolver.class}
                )
                public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
                    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
                    resolver.setContentNegotiationManager(
          (ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.
        class)); resolver.setOrder(-2147483648); return resolver; } ...... }


        - ContentNegotiatingViewResolver:只配置一个@RequestMapping, 匹配多个 url

    • public class ContentNegotiatingViewResolver implements ViewResolver, Ordered, InitializingBean { 
          @Nullable
          privateList<ViewResolver> viewResolvers;   //视图解析器
      
          //初始化方法
          protected void initServletContext(ServletContext servletContext) {
              //从容器中根据ViewResolver类型获取所有的视图解析器Collection<ViewResolver> matchingBeans = BeanFactoryUtils
                      .beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
              ViewResolver viewResolver;
              if (this.viewResolvers == null) {
                  this.viewResolvers = new ArrayList(matchingBeans.size());
                  Iterator var3 = matchingBeans.iterator();
      
                  //遍历所有的视图解析器
                  while(var3.hasNext()) {
                      viewResolver = (ViewResolver)var3.next();
                      if (this != viewResolver) {
                          //将所有的视图解析器加入到本地变量
                          this.viewResolvers.add(viewResolver);
                      }
                  }
              } else {
                  for(int i = 0; i < this.viewResolvers.size(); ++i) {
                      viewResolver = (ViewResolver)this.viewResolvers.get(i);
                      if (!matchingBeans.contains(viewResolver)) {
                          String name = viewResolver.getClass().getName() + i;
                          this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
                      }
                  }
              }
      
              AnnotationAwareOrderComparator.sort(this.viewResolvers);
             this.cnmFactoryBean.setServletContext(servletContext);
          }
      
      
          @Nullable
          publicView 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) {//只配置一个@RequestMapping, 匹配多个 url,所以要获取多个
                  List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);//获取所有候选的视图对象
                  View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);//获取最佳视图对象(根据参数或类型选取)
                  if (bestView != null) {
                      return bestView;
                  }
              }
      
      }
    • 自定义ViewResolver加入到Spring容器
    • //springBoot启动类注解,包含@Configuration
      @SpringBootApplication
      public class DemoApplication {
          public static void main(String[] args) {
              SpringApplication.run(DemoApplication.class, args);
          }
          //将自定义的ViewResolver加入到Spring容器中
          @Bean
          public ViewResolver myViewResolver() {
              return new MyViewResover();
          }
          //自定义ViewResover
          public static class MyViewResover implements  ViewResolver {
              @Nullable
              @Override
              public View resolveViewName(String s, Locale locale) throws Exception {
                  return null;
              }
          }
      }
    •  当SpringBoot项目启动,随便发一个请求,进到DispatcherServlet的DoDispatch()方法时,能够获取到所有的ViewResolver,包括自定义的,被包含在了ContentNegotiatingViewResolver里

         


      - 总结:如何定制视图解析器ViewResolver?

        根据@WebMvcAutoConfiguration

            ——》 @Bean ContentNegotiatingViewResolver viewResolver()

                ——》initServletContext()中根据类型加载容器中所有的ViewResolver

        我们可以自己给容器中添加一个视图解析器,@Bean加入到容器中,SpringBoot便自动的将其组合进来;

      关于视图解析器详情在SprintMVC知识点中学习


     这些前面章节已有学过

      - Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars

      - Static `index.html` support. 静态首页访问

      - Custom `Favicon` support (see below). favicon.ico


    - 自动注册了 of `Converter`, `GenericConverter`, `Formatter` beans.

      - Converter:转换器; public String hello(User user):类型转换使用Converter      (如果页面传参和User对象类型一一对应,不用转换,如果对应不上就需要转换)
      - `Formatter` 格式化器; 2017.12.17   /   2017-12-17  / 2017/12/17===Date;  (1:字符串转日期;2:按照给定的格式转)

      •     
    @Configuration
    @ConditionalOnWebApplication(
            type = ConditionalOnWebApplication.Type.SERVLET
    )
    @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
    @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
    @AutoConfigureOrder(-2147483638)
    @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
    public class WebMvcAutoConfiguration {
    
        @Configuration
        @Import({org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
        @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
        @Order(0)
        public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
            private final ListableBeanFactory beanFactory;
    
            @Bean
            public FormattingConversionService mvcConversionService() {
                WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
                this.addFormatters(conversionService);
                return conversionService;
            }
    
            protected void addFormatters(FormatterRegistry registry) {
                this.configurers.addFormatters(registry);
            }
    
            //添加格式化器
            public void addFormatters(FormatterRegistry registry) {
                //容器中符合Converter类型的bean都被格式化注册器FormatterRegistry注册进来
                Iterator var2 = this.getBeansOfType(Converter.class).iterator();
    
                while(var2.hasNext()) {
                    Converter<?, ?> converter = (Converter)var2.next();
                    registry.addConverter(converter);
                }
    
                //容器中符合GenericConverter类型的bean都被格式化注册器FormatterRegistry注册进来
                var2 = this.getBeansOfType(GenericConverter.class).iterator();
    
                while(var2.hasNext()) {
                    GenericConverter converter = (GenericConverter)var2.next();
                    registry.addConverter(converter);
                }
    
                //容器中符合Formatter类型的bean都被格式化注册器FormatterRegistry注册进来
                var2 = this.getBeansOfType(Formatter.class).iterator();
    
                while(var2.hasNext()) {
                    Formatter<?> formatter = (Formatter)var2.next();
                    registry.addFormatter(formatter);
                }
    
                /**
                 *  从容器中获取所有从属于此类型的Bean
                 *  自己添加的格式化器转换器,我们只需要放在容器中即可
              */
                private <T> Collection<T> getBeansOfType(Class<T> type) {
                    return this.beanFactory.getBeansOfType(type).values();
                }
            }
        }
    }

     

      - HttpMessageConverter:SpringMVC用来转换Http请求和响应的;返回结果将  User    ——》转化为  Json;

      - `HttpMessageConverters` 是从容器中确定;获取所有的HttpMessageConverter;

    •            
    • //@Configuration包含了@Component
      @Configuration
      public class WebMvcAutoConfiguration {
          public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
              private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
      
              //通过构造方法从容器中注入进来
              public WebMvcAutoConfigurationAdapter(ObjectProvider<HttpMessageConverters> messageConvertersProvider,其它参数) {
                  this.messageConvertersProvider = messageConvertersProvider;
              ......
              }
          }
      }
      
      //@Configuration包含了@Component
      @Configuration
      public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {
          private final List<HttpMessageConverter<?>> converters;
      
          //
          public HttpMessageConverters(HttpMessageConverter... additionalConverters) {
              this((Collection) Arrays.asList(additionalConverters));
          }
          //通过构造方法从容器中注入进来
          public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) {
              this(true, additionalConverters);
          }
      }

       自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)

      - Automatic registration of `MessageCodesResolver` (see below).定义错误代码生成规则

      - Automatic use of a `ConfigurableWebBindingInitializer` bean (see below).

        我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)

          ```初始化WebDataBinder;
          ```请求数据=====JavaBean;

    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 RequestMappingHandlerMappingRequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

    If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.


     总结:如何修改SpringBoot的默认配置?

    ​   1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,

        如果没有,才自动配置;

        如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;

  • 相关阅读:
    实验一 命令解释程序的编写
    试验二
    实验一 命令解释程序的编写(重交)
    Sqlserver数据库帮助类(EFTools)
    js验证
    sqlserver中从日期字段取得月份
    IIS不可用或者有问题解决方法
    professional email address collections
    从psd文件到html
    空白符对HTML结构的影响与解决方案
  • 原文地址:https://www.cnblogs.com/guchunchao/p/9898270.html
Copyright © 2020-2023  润新知