springboot web应用(二):WebMvc 装配过程

321 阅读9分钟

注:本系列源码分析基于springboot 2.2.2.RELEASE,对应的spring版本为5.2.2.RELEASE,源码的gitee仓库仓库地址:gitee.com/funcy/sprin….

平时工作中,springboot 绝大多数情况下运行的是web项目,本文将分析 springboot 自动 springMvc 项目的流程。

1. springMvc 的自动装配类

springMvc 的自动装配类为

@Configuration(proxyBeanMethods = false)
// 几个装配条件
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 如果没有自定义WebMvc的配置类,则使用本配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
// 装配顺序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ 
        // DispatcherServlet 的自动装配
        DispatcherServletAutoConfiguration.class, 
        // 线程池的自动装配
        TaskExecutionAutoConfiguration.class,
        // jsr 303 验证框架的自动装配
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    ...
}

从上面的@AutoConfigureAfter注解中,WebMvcAutoConfiguration 需要在 DispatcherServletAutoConfigurationTaskExecutionAutoConfigurationValidationAutoConfiguration 等类装配完成之后再装配,对这些类的作用列举如下:

  • DispatcherServletAutoConfigurationDispatcherServlet 自动装配
  • TaskExecutionAutoConfiguration:任务执行器,其实就是创建了一个线程池
  • ValidationAutoConfiguration:jsr 303 验证器的自动装配,验证器用来处理@NotNull@NotEmpty等注解的验证功能

这3个类中,与 springMvc 有关的只有DispatcherServletAutoConfiguration,我们来认识一下它:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
// 需要在 ServletWebServerFactoryAutoConfiguration 自动装配完成后处理
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    ...
}

DispatcherServletAutoConfiguration需要等ServletWebServerFactoryAutoConfiguration自动装配完成才进行装配,这个类是做什么的呢?剧透下,它是处理 servlet 容器(tomcat, jetty, undertow等)的生成的,我们再来看看ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
// 引入了一些类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        // 3个 web 容器
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    ...
}

可以看到,这个类中总算是没有@AutoConfigureAfter注解了,因此这个类 springMvc 就是最初自动装配的类,我们的分析就从这个类开始。

总结下以上几个类的装配顺序:

  1. ServletWebServerFactoryAutoConfiguration
  2. DispatcherServletAutoConfiguration
  3. WebMvcAutoConfiguration

接下来我们的分析也按这样的顺序,逐一分析这些自动装配类。

2. ServletWebServerFactoryAutoConfiguration 的自动装配

ServletWebServerFactoryAutoConfiguration 类如下:

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
// 引入了一些类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        // 3个 web 容器
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    ...
}

这个类引入了BeanPostProcessorsRegistrarEmbeddedTomcatEmbeddedJettyEmbeddedUndertow,我们来逐一分析吧!

2.1 BeanPostProcessorsRegistrar

BeanPostProcessorsRegistrarServletWebServerFactoryAutoConfiguration 的内部类,代码如下:

public static class BeanPostProcessorsRegistrar 
        implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

    ...

    /**
     * 来自 ImportBeanDefinitionRegistrar 的方法
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registry) {
        if (this.beanFactory == null) {
            return;
        }
        // 注册组件
        registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
                WebServerFactoryCustomizerBeanPostProcessor.class);
        registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
                ErrorPageRegistrarBeanPostProcessor.class);
    }
    
    /**
     * 具体的注册操作
     */
    private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, 
            String name, Class<?> beanClass) {
        if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
            beanDefinition.setSynthetic(true);
            registry.registerBeanDefinition(name, beanDefinition);
        }
    }
}

这个类主要是向 spring 容器中注册了两个类:WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor,我们来看下它们分别是个啥。

1. WebServerFactoryCustomizerBeanPostProcessor

WebServerFactoryCustomizerBeanPostProcessor 的代码如下:

public class WebServerFactoryCustomizerBeanPostProcessor 
        implements BeanPostProcessor, BeanFactoryAware {

    ...

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
            throws BeansException {
        // bean 的类型是 WebServerFactory 才处理
        if (bean instanceof WebServerFactory) {
            postProcessBeforeInitialization((WebServerFactory) bean);
        }
        return bean;
    }

    @SuppressWarnings("unchecked")
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
                .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                // 处理配置
                .invoke((customizer) -> customizer.customize(webServerFactory));
    }

    /**
     * 获取 WebServerFactoryCustomizer,得到的是一个不可变的 List
     */
    private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
        if (this.customizers == null) {
            this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
            this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }
        return this.customizers;
    }

    /**
     * 获取 beanFactory 中所有的 WebServerFactoryCustomizer
     */
    private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
        return (Collection) this.beanFactory.getBeansOfType(
                WebServerFactoryCustomizer.class, false, false).values();
    }

}

这个类是用来处理WebServerFactory的自定义配置的,它实现了BeanPostProcessor, 我们主要关注它的postProcessBeforeInitialization(...)方法。

WebServerFactoryCustomizerBeanPostProcessorpostProcessBeforeInitialization(...)方法中,如果当前bean的类型是 WebServerFactory,会先获取beanFactory中所有类型为WebServerFactoryCustomizer的bean,然后将这些WebServerFactoryCustomizer配置到WebServerFactorybean中。

如果我们要自定义Tomcat的配置,可以这样处理:

@Component
public class MyCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    
    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.setPort(8091);
        factory.setContextPath("/");
        // 设置其他配置
        ...
    }
    
}

ServletWebServerFactoryAutoConfiguration中提供了两个WebServerFactoryCustomizer:

public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    ...

}

从方法参数来看,这两个类的配置都来自于ServerProperties

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    ...
}

它上面了ConfigurationProperties注解,prefix 为 "server",这表明它的配置都是以server开头,支持的配置如下:

例如,我们要配置它的端口,只需在application.properties中这样配置即可:

server.port=8080

至于其他配置,这里就不多作分析了。

2. ErrorPageRegistrarBeanPostProcessor

ErrorPageRegistrarBeanPostProcessor的代码如下:

public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    /**
     * 来自BeanPostProcessor的方法,在bean初始化前执行
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
            throws BeansException {
        if (bean instanceof ErrorPageRegistry) {
            // 如果 bean 是 ErrorPageRegistry
            postProcessBeforeInitialization((ErrorPageRegistry) bean);
        }
        return bean;
    }

    private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
        for (ErrorPageRegistrar registrar : getRegistrars()) {
            // 注册错误页
            registrar.registerErrorPages(registry);
        }
    }

    private Collection<ErrorPageRegistrar> getRegistrars() {
        if (this.registrars == null) {
            // 获取所有的错误页
            this.registrars = new ArrayList<>(this.beanFactory.getBeansOfType(
                    ErrorPageRegistrar.class, false, false).values());
            this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.registrars = Collections.unmodifiableList(this.registrars);
        }
        return this.registrars;
    }

    ...
}

我们主要关注postProcessBeforeInitialization的流程:

  1. 如果 bean 的类型是 ErrorPageRegistry,进行第2步
  2. 获取 beanFactory 中所有ErrorPageRegistrar类型的bean,遍历进行第3步
  3. 调用registrar.registerErrorPages(registry)进行错误页操作

这里有两个类需要说明:

  • ErrorPageRegistrar:错误页注册器(让ErrorPageRegistry干活的类)
  • ErrorPageRegistry:错误的注册器类(实际干活的类)

如果我们想要自定义错误页,可以实现ErrorPageRegistry接口:

@Component
public class MyErrorPage implements ErrorPageRegistrar {

    /**
     * 注册错误页
     */
    @Override
    public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
        // 最终调用的是 ErrorPageRegistry#addErrorPages 进行注册
        errorPageRegistry.addErrorPages(new ErrorPage("/error/page"));
    }
}

2.2 EmbeddedTomcat

进入ServletWebServerFactoryConfiguration.EmbeddedTomcat类:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

    @Configuration(proxyBeanMethods = false)
    // 条件注解,条件存在时才引入
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    // 注意 ServletWebServerFactory,我们可以自主实现 tomcat 容器的装配
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, 
        search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory(
                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
                ObjectProvider<TomcatContextCustomizer> contextCustomizers,
                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            // 配置一些参数
            factory.getTomcatConnectorCustomizers()
                    .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers()
                    .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers()
                    .addAll(protocolHandlerCustomizers.orderedStream()
                    .collect(Collectors.toList()));
            return factory;
        }

    }

    ...
}

这个类主要就是返回TomcatServletWebServerFactory bean,可以注入一些connectorCustomizerscontextCustomizersprotocolHandlerCustomizers等参数进行自定义配置,这些参数就是从BeanPostProcessorsRegistrar中来的。

这里有个地方需要提一下,如果不想使用 springboot 提供的TomcatServletWebServerFactory,我们可以自己实现TomcatServletWebServerFactory,像这样:

@Bean
public ServletWebServerFactory servletWebServerFactory() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    // 处理自定义的各种配置
    ...
    return tomcat;
}

这样处理之后,springboot提供的 tomcatServletWebServerFactory 就不会处理了。

其他两个类EmbeddedJettyEmbeddedUndertow,跟EmbeddedTomcat的处理基本相似,就不多说了。

3. DispatcherServletAutoConfiguration

我们再来看看DispatcherServletAutoConfiguration,关键代码如下:

public class DispatcherServletAutoConfiguration {

    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

    public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME 
            = "dispatcherServletRegistration";

    @Configuration(proxyBeanMethods = false)
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
    protected static class DispatcherServletConfiguration {

        /**
         * 大名鼎鼎的 DispatcherServlet.
         * @param httpProperties http属性.
         * @param webMvcProperties webMvc 属性.
         * @return 返回对象
         */
        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, 
                WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(
                    webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(
                    webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

        /**
         * 文件上传组件.
         * @param resolver 参数.
         * @return 返回值.
         */
        @Bean
        @ConditionalOnBean(MultipartResolver.class)
        @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            return resolver;
        }

    }

    /**
     * 生成 DispatcherServletRegistrationBean
     * 它会将dispatcherServlet注册到servlet容器
     */
    @Configuration(proxyBeanMethods = false)
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, 
                name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, 
                ObjectProvider<MultipartConfigElement> multipartConfig) {
            // 生成 DispatcherServletRegistrationBean(它会将dispatcherServlet注册到servlet容器)
            DispatcherServletRegistrationBean registration = 
                    new DispatcherServletRegistrationBean(dispatcherServlet, 
                    webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

    }

    ...

}

它主要是注册了3个bean:

  • dispatcherServlet:springMvc 的请求入口,url 请求由此进入,然后转化到 requestMapping
  • multipartResolver:处理文件上传
  • dispatcherServletRegistration:处理dispatcherServlet的注册,它会将dispatcherServlet注册到servlet容器中(关于springboot注册servlet组件的内容,可以参考springboot web应用之servlet 组件的注册流程

4. WebMvcAutoConfiguration

继续看WebMvcAutoConfiguration:

@Configuration(proxyBeanMethods = false)
...
// 如果没有自定义WebMvc的配置类,则使用本配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
...
public class WebMvcAutoConfiguration {
    ...
}

WebMvcAutoConfiguration上有个注解需要注意下:

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

这行代码表明了WebMvcAutoConfiguration只有在WebMvcConfigurationSupport类型的bean不存在时,它才会进行自动装配。

WebMvcConfigurationSupport是个啥呢?在springmvc demo 与 @EnableWebMvc 注解一文中,我们提到引入springMvc组件的两种方式:

  • 使用@EnableWebMvc注解
  • 继承WebMvcConfigurationSupport

而这种方法最终都会向spring容器中引入类型为WebMvcConfigurationSupport的 bean,因此在springboot项目中,如果我们进行了以上两种操作之一,那么就引入了WebMvcConfigurationSupportWebMvcAutoConfiguration的自动装配就不执行了。

我们再来看看WebMvcAutoConfiguration装配的bean。

4.1 WebMvcAutoConfigurationAdapter

WebMvcAutoConfigurationAdapterWebMvcAutoConfiguration的内部类,定义如下:

@Configuration(proxyBeanMethods = false)
// 引入了 EnableWebMvcConfiguration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    ...
}

它实现了WebMvcConfigurer,且引入了EnableWebMvcConfigurationWebMvcConfigurer可以用来处理springMvc的配置,只需要重写其中对应的方法即可,EnableWebMvcConfiguration从名称来看,是“启用webMvc配置”,它也是WebMvcAutoConfiguration的内部类,我们来看看它做了啥:

@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration 
        implements ResourceLoaderAware {
    ...
}

可以看到,它是DelegatingWebMvcConfiguration的子类,而DelegatingWebMvcConfiguration又是个啥呢?我们也来看看它的定义:

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    ...
}

DelegatingWebMvcConfiguration是由 springMvc 提供的,可以看到,它实现了WebMvcConfigurationSupport,因此,springboot 通过@Import(EnableWebMvcConfiguration.class)的方式向spring容器中引入了WebMvcConfigurationSupport类型的bean。

事实上,DelegatingWebMvcConfiguration 正是@EnableWebMvc引入的bean:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 引入了 DelegatingWebMvcConfiguration
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
    ...
}

springbot 不直接引入DelegatingWebMvcConfiguration而是引入它的子类EnableWebMvcConfiguration,虽然它是做了一些自定义配置的。

关于EnableWebMvcConfiguration配置了些啥,我们一会再分析,继续分析WebMvcAutoConfigurationAdapter的引入bean:

/**
 * http 消息转换器.
 */
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    this.messageConvertersProvider.ifAvailable(
            (customConverters) -> converters.addAll(customConverters.getConverters()));
}

/**
 * 视图解析器.
 */
@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(Ordered.LOWEST_PRECEDENCE - 10);
    return resolver;
}

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

/**
 * 静态资源映射
 */
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    // 映射webjars
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache()
            .getCachecontrol().toHttpCacheControl();
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/")
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    // 映射静态资源路径
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                // staticLocations 默认为 classpath:[/META-INF/resources/,
                // /resources/, /static/, /public/]
                .addResourceLocations(getResourceLocations(
                    this.resourceProperties.getStaticLocations()))
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

可以看到,WebMvcAutoConfigurationAdapter引入了三类bean:

  1. http消息转换器
  2. 视图解析器
  3. 国际化配置

这里我们重点来看看http消息转换器。在使用 springMvc 时,通过在Controller类或其中的方法上标记@ResponseBody,返回参数就会转换为json,这就是http消息转换器所做的工作了。springboot 默认的 jaon 处理是jackson,它的实例化在JacksonAutoConfiguration 中,装配为HttpMessageConverter的处理在JacksonHttpMessageConvertersConfiguration,这里就不展开了。

在上面 的分析中,多次出现了WebMvcProperties配置,我们来看看它是个啥:

@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
    ...
}

很明显,它是个配置类 ,可以看到,它的配置前缀为 spring.mvc,支持的配置如下:

4.2 EnableWebMvcConfiguration

我们来看看EnableWebMvcConfiguration自定义的配置:

public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration 
        implements ResourceLoaderAware {

    /**
     * 配置适配器
     */
    @Bean
    @Override
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
            @Qualifier("mvcContentNegotiationManager") 
                ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcValidator") Validator validator) {
        RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(
                contentNegotiationManager, conversionService, validator);
        adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null 
                || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
        return adapter;
    }

    /**
     * 路径映射
     */
    @Bean
    @Primary
    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping(
            @Qualifier("mvcContentNegotiationManager") 
                    ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
        // Must be @Primary for MvcUriComponentsBuilder to work
        return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
                resourceUrlProvider);
    }

    /**
     * 欢迎页
     */
    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(
            ApplicationContext applicationContext, 
            FormattingConversionService mvcConversionService, 
            ResourceUrlProvider mvcResourceUrlProvider) {
        WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
                new TemplateAvailabilityProviders(applicationContext), applicationContext, 
                getWelcomePage(), this.mvcProperties.getStaticPathPattern());
        welcomePageHandlerMapping.setInterceptors(getInterceptors(
                mvcConversionService, mvcResourceUrlProvider));
        return welcomePageHandlerMapping;
    }

    /**
     * mvc配置,添加了日期格式的处理
     */
    @Bean
    @Override
    public FormattingConversionService mvcConversionService() {
        WebConversionService conversionService 
            = new WebConversionService(this.mvcProperties.getDateFormat());
        addFormatters(conversionService);
        return conversionService;
    }

    /**
     * 参数校验器
     */
    @Bean
    @Override
    public Validator mvcValidator() {
        if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
            return super.mvcValidator();
        }
        return ValidatorAdapter.get(getApplicationContext(), getValidator());
    }

    /**
     * 异常处理
     */
    @Override
    protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
        if (this.mvcRegistrations != null 
                && this.mvcRegistrations.getExceptionHandlerExceptionResolver() != null) {
            return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
        }
        return super.createExceptionHandlerExceptionResolver();
    }

    @Override
    protected void extendHandlerExceptionResolvers(
                List<HandlerExceptionResolver> exceptionResolvers) {
        super.extendHandlerExceptionResolvers(exceptionResolvers);
        if (this.mvcProperties.isLogResolvedException()) {
            for (HandlerExceptionResolver resolver : exceptionResolvers) {
                if (resolver instanceof AbstractHandlerExceptionResolver) {
                    ((AbstractHandlerExceptionResolver) resolver).setWarnLogCategory(
                            resolver.getClass().getName());
                }
            }
        }
    }
    ...
}

相比于原生的springMvc,它额外配置了适配器、路径映射、参数校验、异常处理等。

5. 总结

  1. webMvc在装配时,先装配 ServletWebServerFactoryAutoConfiguration,再装配DispatcherServletAutoConfiguration,最后装配 WebMvcAutoConfiguration
  2. ServletWebServerFactoryAutoConfiguration 处理 servlet 容器的装配,DispatcherServletAutoConfiguration处理DispatcherServlet的装配,WebMvcAutoConfiguration处理 webMvc 组件(消息转换器、视图解析器、静态资源映射等)的装配
  3. 如果向spring容器中引入了WebMvcConfigurationSupportWebMvcAutoConfiguration 的装配操作将不会执行
  4. servlet的相关配置以servlet为前缀,webMvc的配置以spring.mvc为前缀,我们可以在配置文件(一般为application.properties/application.yml)中进行配置

本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。

【springboot源码分析】springboot源码分析系列文章汇总