springboot源碼解析autoconfigure之WebMvcAutoConfiguration

說在前面html

本次開始spring-boot-autoconfigure源碼解析之WebMvcAutoConfigurationjava

 

源碼解析web

@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 {

   public static final String DEFAULT_PREFIX = "";
   public static final String DEFAULT_SUFFIX = "";
   @Bean
   @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
   public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
      return new OrderedHiddenHttpMethodFilter();
   }

   @Bean
   @ConditionalOnMissingBean(HttpPutFormContentFilter.class)
   @ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
   public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
      return new OrderedHttpPutFormContentFilter();
   }

   // Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not 定義爲嵌套配置,以確保不讀取WebMvcConfigurerAdapter
   // on the classpath 在類路徑中
   @Configuration
   @Import(EnableWebMvcConfiguration.class)
   @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
   public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {

      private static final Log logger = LogFactory
            .getLog(WebMvcConfigurerAdapter.class);
      private final ResourceProperties resourceProperties;
      private final WebMvcProperties mvcProperties;
      private final ListableBeanFactory beanFactory;
      private final HttpMessageConverters messageConverters;
      final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
      public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
            WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
            @Lazy HttpMessageConverters messageConverters,
            ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
         this.resourceProperties = resourceProperties;
         this.mvcProperties = mvcProperties;
         this.beanFactory = beanFactory;
         this.messageConverters = messageConverters;
         this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
               .getIfAvailable();
      }

      @Override
      public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
         converters.addAll(this.messageConverters.getConverters());
      }

      @Override
      public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
         Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
         if (timeout != null) {
            configurer.setDefaultTimeout(timeout);
         }
      }

      @Override
      public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
         Map<String, MediaType> mediaTypes = this.mvcProperties.getMediaTypes();
         for (Entry<String, MediaType> mediaType : mediaTypes.entrySet()) {
            configurer.mediaType(mediaType.getKey(), mediaType.getValue());
         }
      }

//    初始化InternalResourceViewResolver
      @Bean
      @ConditionalOnMissingBean
      public InternalResourceViewResolver defaultViewResolver() {
         InternalResourceViewResolver resolver = new InternalResourceViewResolver();
         resolver.setPrefix(this.mvcProperties.getView().getPrefix());
         resolver.setSuffix(this.mvcProperties.getView().getSuffix());
         return resolver;
      }

//    初始化BeanNameViewResolver
      @Bean
      @ConditionalOnBean(View.class)
      @ConditionalOnMissingBean
      public BeanNameViewResolver beanNameViewResolver() {
         BeanNameViewResolver resolver = new BeanNameViewResolver();
         resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
         return resolver;
      }

//    初始化ContentNegotiatingViewResolver
      @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 contentatingviewresolver使用全部其餘視圖解析器來定位
         // 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;
      }

      @Bean
      @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
      public Formatter<Date> dateFormatter() {
         return new DateFormatter(this.mvcProperties.getDateFormat());
      }

      @Override
      public MessageCodesResolver getMessageCodesResolver() {
         if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
            DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
            resolver.setMessageCodeFormatter(
                  this.mvcProperties.getMessageCodesResolverFormat());
            return resolver;
         }
         return null;
      }

      @Override
      public void addFormatters(FormatterRegistry registry) {
         for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
            registry.addConverter(converter);
         }
         for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
            registry.addConverter(converter);
         }
         for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
            registry.addFormatter(formatter);
         }
      }

      private <T> Collection<T> getBeansOfType(Class<T> type) {
         return this.beanFactory.getBeansOfType(type).values();
      }

      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
         if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
         }
         Integer cachePeriod = this.resourceProperties.getCachePeriod();
         if (!registry.hasMappingForPattern("/webjars/**")) {
            customizeResourceHandlerRegistration(registry
                  .addResourceHandler("/webjars/**")
                  .addResourceLocations("classpath:/META-INF/resources/webjars/")
                  .setCachePeriod(cachePeriod));
         }
         String staticPathPattern = this.mvcProperties.getStaticPathPattern();
         if (!registry.hasMappingForPattern(staticPathPattern)) {
            customizeResourceHandlerRegistration(
                  registry.addResourceHandler(staticPathPattern)
                        .addResourceLocations(
                              this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
         }
      }

      @Bean
      public WelcomePageHandlerMapping welcomePageHandlerMapping(
            ResourceProperties resourceProperties) {
         return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
               this.mvcProperties.getStaticPathPattern());
      }

      private void customizeResourceHandlerRegistration(
            ResourceHandlerRegistration registration) {
         if (this.resourceHandlerRegistrationCustomizer != null) {
            this.resourceHandlerRegistrationCustomizer.customize(registration);
         }

      }

      @Bean
      @ConditionalOnMissingBean({ RequestContextListener.class,
            RequestContextFilter.class })
      public static RequestContextFilter requestContextFilter() {
         return new OrderedRequestContextFilter();
      }

      @Configuration
      @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
      public static class FaviconConfiguration {

         private final ResourceProperties resourceProperties;
         public FaviconConfiguration(ResourceProperties resourceProperties) {
            this.resourceProperties = resourceProperties;
         }

//       初始化SimpleUrlHandlerMapping
         @Bean
         public SimpleUrlHandlerMapping faviconHandlerMapping() {
            SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
            mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
            mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
                  faviconRequestHandler()));
            return mapping;
         }

         @Bean
         public ResourceHttpRequestHandler faviconRequestHandler() {
            ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
            requestHandler
                  .setLocations(this.resourceProperties.getFaviconLocations());
            return requestHandler;
         }

      }

   }

   /**
    * Configuration equivalent to {@code @EnableWebMvc}.
    */
   @Configuration
   public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

      private final WebMvcProperties mvcProperties;
      private final ListableBeanFactory beanFactory;
      private final WebMvcRegistrations mvcRegistrations;
      public EnableWebMvcConfiguration(
            ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
            ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
            ListableBeanFactory beanFactory) {
         this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
         this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
         this.beanFactory = beanFactory;
      }

//    建立RequestMappingHandlerAdapter
      @Bean
      @Override
      public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
         RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
         adapter.setIgnoreDefaultModelOnRedirect((this.mvcProperties != null)
               ? this.mvcProperties.isIgnoreDefaultModelOnRedirect() : true);
         return adapter;
      }

      @Override
      protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
         if (this.mvcRegistrations != null
               && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
            return this.mvcRegistrations.getRequestMappingHandlerAdapter();
         }
         return super.createRequestMappingHandlerAdapter();
      }

//    建立RequestMappingHandlerMapping
      @Bean
      @Primary
      @Override
      public RequestMappingHandlerMapping requestMappingHandlerMapping() {
         // Must be @Primary for MvcUriComponentsBuilder to work
         return super.requestMappingHandlerMapping();
      }

//    建立Validator
      @Bean
      @Override
      public Validator mvcValidator() {
         if (!ClassUtils.isPresent("javax.validation.Validator",
               getClass().getClassLoader())) {
            return super.mvcValidator();
         }
         return WebMvcValidator.get(getApplicationContext(), getValidator());
      }

      @Override
      protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
         if (this.mvcRegistrations != null
               && this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
            return this.mvcRegistrations.getRequestMappingHandlerMapping();
         }
         return super.createRequestMappingHandlerMapping();
      }

      @Override
      protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
         try {
            return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
         }
         catch (NoSuchBeanDefinitionException ex) {
            return super.getConfigurableWebBindingInitializer();
         }
      }

      @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());
               }
            }
         }
      }

//     建立ContentNegotiationManager
      @Bean
      @Override
      public ContentNegotiationManager mvcContentNegotiationManager() {
         ContentNegotiationManager manager = super.mvcContentNegotiationManager();
         List<ContentNegotiationStrategy> strategies = manager.getStrategies();
         ListIterator<ContentNegotiationStrategy> iterator = strategies.listIterator();
         while (iterator.hasNext()) {
            ContentNegotiationStrategy strategy = iterator.next();
            if (strategy instanceof PathExtensionContentNegotiationStrategy) {
               iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
                     strategy));
            }
         }
         return manager;
      }

   }

   @Configuration
   @ConditionalOnEnabledResourceChain
   static class ResourceChainCustomizerConfiguration {

      @Bean
      public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
         return new ResourceChainResourceHandlerRegistrationCustomizer();
      }

   }

   interface ResourceHandlerRegistrationCustomizer {

      void customize(ResourceHandlerRegistration registration);
   }

   private static class ResourceChainResourceHandlerRegistrationCustomizer
         implements ResourceHandlerRegistrationCustomizer {

      @Autowired
      private ResourceProperties resourceProperties = new ResourceProperties();
      @Override
      public void customize(ResourceHandlerRegistration registration) {
         ResourceProperties.Chain properties = this.resourceProperties.getChain();
         configureResourceChain(properties,
               registration.resourceChain(properties.isCache()));
      }

      private void configureResourceChain(ResourceProperties.Chain properties,
            ResourceChainRegistration chain) {
         Strategy strategy = properties.getStrategy();
         if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
            chain.addResolver(getVersionResourceResolver(strategy));
         }
         if (properties.isGzipped()) {
            chain.addResolver(new GzipResourceResolver());
         }
         if (properties.isHtmlApplicationCache()) {
            chain.addTransformer(new AppCacheManifestTransformer());
         }
      }

      private ResourceResolver getVersionResourceResolver(
            ResourceProperties.Strategy properties) {
         VersionResourceResolver resolver = new VersionResourceResolver();
         if (properties.getFixed().isEnabled()) {
            String version = properties.getFixed().getVersion();
            String[] paths = properties.getFixed().getPaths();
            resolver.addFixedVersionStrategy(version, paths);
         }
         if (properties.getContent().isEnabled()) {
            String[] paths = properties.getContent().getPaths();
            resolver.addContentVersionStrategy(paths);
         }
         return resolver;
      }

   }

   static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {

      private static final Log logger = LogFactory
            .getLog(WelcomePageHandlerMapping.class);
      private WelcomePageHandlerMapping(Resource welcomePage,
            String staticPathPattern) {
         if (welcomePage != null && "/**".equals(staticPathPattern)) {
            logger.info("Adding welcome page: " + welcomePage);
            ParameterizableViewController controller = new ParameterizableViewController();
//          WelcomePageHandlerMapping直接轉發index.html
            controller.setViewName("forward:index.html");
            setRootHandler(controller);
            setOrder(0);
         }
      }

      @Override
      public Object getHandlerInternal(HttpServletRequest request) throws Exception {
         for (MediaType mediaType : getAcceptedMediaTypes(request)) {
            if (mediaType.includes(MediaType.TEXT_HTML)) {
               return super.getHandlerInternal(request);
            }
         }
         return null;
      }

      private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) {
         String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
         return MediaType.parseMediaTypes(
               StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*");
      }

   }

   /**
    * Decorator to make {@link PathExtensionContentNegotiationStrategy} optional
    * depending on a request attribute.
    */
   static class OptionalPathExtensionContentNegotiationStrategy
         implements ContentNegotiationStrategy {

      private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class
            .getName() + ".SKIP";
      private final ContentNegotiationStrategy delegate;
      OptionalPathExtensionContentNegotiationStrategy(
            ContentNegotiationStrategy delegate) {
         this.delegate = delegate;
      }

      @Override
      public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
            throws HttpMediaTypeNotAcceptableException {
         Object skip = webRequest.getAttribute(SKIP_ATTRIBUTE,
               RequestAttributes.SCOPE_REQUEST);
         if (skip != null && Boolean.parseBoolean(skip.toString())) {
            return Collections.emptyList();
         }
         return this.delegate.resolveMediaTypes(webRequest);
      }

   }

}

 

說在最後spring

本次僅表明我的觀點,僅供參考。mvc

相關文章
相關標籤/搜索