DispatcherServlet自动配置_源码分析

在使用SpringBoot之后,我们表面上已经无法直接看到DispatcherServlet的使用了。其实SpringBoot中DispatcherServlet是根据DispatcherServletAutoConfiguration自动化配置的。

先看注解部分代码:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    ...
}
  • @AutoConfigureOrder指定该自动配置的优先级;
  • @Configuration指定该类为自动配置类;
  • @ConditionalOnWebApplication指定自动配置需要满足是基于SERVLET的web应用;
  • @ConditionalOnClass指定类路径下必须有DispatcherServlet类存在;
  • @AutoConfigureAfter指定该自动配置必须基于ServletWebServerFactoryAutoConfiguration的自动配置。

DispatcherServletAutoConfiguration中关于DispatcherServlet实例化的代码如下:

@Configuration(proxyBeanMethods = false) // 实例化配置类
@Conditional(DefaultDispatcherServletCondition.class) // 实例化条件:通过该类来判断
@ConditionalOnClass(ServletRegistration.class) // 存在指定的ServletRegistration类
// 加载HttpProperties和WebMvcProperties
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {

    @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
        // 创建DispatcherServlet
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        // 初始化DispatcherServlet各项配置
        dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
        dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
        dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
        dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
        return dispatcherServlet;
    }

    // 初始化上传文件的解析器
    @Bean
    @ConditionalOnBean(MultipartResolver.class)
    @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
    public MultipartResolver multipartResolver(MultipartResolver resolver) {
        // Detect if the user has created a MultipartResolver but named it incorrectly
        return resolver;
    }

}

内部类DispatcherServletConfiguration同样需要满足指定的条件才会进行初始化,具体看代码中的注释。

其中的dispatcherServlet方法中实现了DispatcherServlet的实例化,并设置了基础参数。这对照传统的配置就是web.xml中DispatcherServlet的配置。

另外一个方法multipartResolver,用于初始化上传文件的解析器,主要作用是当用户定义的MultipartResolver名字不为"multipartResolver"时,通过该方法将其修改为"multipartResolver",相当于重命名。

其中DispatcherServletConfiguration的注解@Conditional限定必须满足DefaultDispatcherServletCondition定义的匹配条件才会自动配置。而DefaultDispatcherServletCondition类同样为内部类。

@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        List<String> dispatchServletBeans = Arrays
            .asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
        if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
            return ConditionOutcome
                .noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }
        if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
            return ConditionOutcome.noMatch(
                message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }
        if (dispatchServletBeans.isEmpty()) {
            return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
        }
        return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
                                      .items(Style.QUOTE, dispatchServletBeans)
                                      .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    }

}

该类的核心功能,总结起来就是:检验Spring容器中是否已经存在一个名字为"dispatcherServlet"的DispatcherServlet,如果不存在,则满足条件。

在该自动配置类中还有用于实例化ServletRegistrationBean的内部类:

@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) {
		// 通过ServletRegistrationBean将dispatcherServlet注册为servlet,这样servlet才会生效。
		DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
				webMvcProperties.getServlet().getPath());
		// 设置名称为dispatcherServlet
		registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
		// 设置加载优先级,设置值默认为-1,存在于WebMvcProperties类中
		registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
		multipartConfig.ifAvailable(registration::setMultipartConfig);
		return registration;
	}

}

DispatcherServletRegistrationConfiguration类的核心功能就是注册dispatcherServlet使其生效并设置一些初始化的参数。

其中,DispatcherServletRegistrationBean继承自ServletRegistrationBean,主要为DispatcherServlet提供服务。DispatcherServletRegistrationBean和DispatcherServlet都提供了注册Servlet并公开DispatcherServletPath信息的功能。

Spring Boot通过上面的自动配置类就完成了之前我们在web.xml中的配置操作。这也是它的方便之处。

 

posted @ 2022-01-21 16:37  残城碎梦  阅读(290)  评论(1编辑  收藏  举报