Tomcat 9 源码解析 -- 与SpringMvc如何结合在一起
Tomcat启动项目的方式下,如何加载SpringMvc 中的 DispatcherServletSpringBootServletInitializer类public abstract class SpringBootServletInitializer implements WebApplicationInitializer {protected Log logger; // Don't in
Tomcat启动项目的方式下,如何加载SpringMvc 中的 DispatcherServlet
SpringBootServletInitializer类
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
protected Log logger; // Don't initialize early
private boolean registerErrorPageFilter = true;
/**
* Set if the {@link ErrorPageFilter} should be registered. Set to {@code false} if
* error page mappings should be handled via the server and not Spring Boot.
* @param registerErrorPageFilter if the {@link ErrorPageFilter} should be registered.
*/
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
this.registerErrorPageFilter = registerErrorPageFilter;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
// 创建IOC容器
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
// 创建Spring应用构建器,并进行相关属性设置
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
//调用configure方法,创建war类型的web项目后,由于编写SpringBootServletInitializer的
//子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
//通过构建器构建了一个Spring应用
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
//启动Spring应用
return run(application);
}
/**
* Returns the {@code SpringApplicationBuilder} that is used to configure and create
* the {@link SpringApplication}. The default implementation returns a new
* {@code SpringApplicationBuilder} in its default state.
* @return the {@code SpringApplicationBuilder}.
* @since 1.3.0
*/
protected SpringApplicationBuilder createSpringApplicationBuilder() {
return new SpringApplicationBuilder();
}
/**
* Called to run a fully configured {@link SpringApplication}.
* @param application the application to run
* @return the {@link WebApplicationContext}
*/
//Spring应用启动,创建并返回IOC容器
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
}
private ApplicationContext getExistingRootWebApplicationContext(
ServletContext servletContext) {
Object context = servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (context instanceof ApplicationContext) {
return (ApplicationContext) context;
}
return null;
}
/**
* Configure the application. Normally all you would need to do is to add sources
* (e.g. config classes) because other settings have sensible defaults. You might
* choose (for instance) to add default command line arguments, or set an active
* Spring profile.
* @param builder a builder for the application context
* @return the application builder
* @see SpringApplicationBuilder
*/
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder;
}
private static final class WebEnvironmentPropertySourceInitializer
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
private final ServletContext servletContext;
private WebEnvironmentPropertySourceInitializer(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (environment instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) environment)
.initPropertySources(this.servletContext, null);
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
}
SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。
SpringBootServletInitializer类中的 run() 方法:
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
}
此处可以看出 调用了(WebApplicationContext) application.run();调用了
这个方法,此处的 application 为 SpringApplication application 对象传入的参数。也就是调用了SpringApplication的run方法。
SpringApplication的run方法:
Spring Boot应用启动运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
//创建一个ApplicationContext容器
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新IOC容器
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
createApplicationContext();
创建IOC容器,如果是web应用,则创建AnnotationConfigServletWebServerApplicationContext的IOC容器;
如果不是,则创建AnnotationConfigReactiveWebServerApplicationContext的IOC容器;或者 AnnotationConfigApplicationContext的IOC容器
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
此方法创建了 创建AnnotationConfigServletWebServerApplicationContext 对象 IOC容器
refreshContext(context);Spring Boot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中每一个组件】;
SpringApplication#refresh
refresh(context);刷新刚才创建的IOC容器;
private void refreshContext(ConfigurableApplicationContext context) {
// 刷新刚才创建的IOC容器;
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
......
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
调用抽象父类的refresh()方法:
也就是这个类 AnnotationConfigServletWebServerApplicationContext 的父类 AbstractApplicationContext。
他们的继承关系:
/**
* ☆☆☆☆☆ !!!! @@@@@
*
* refresh()方法是整个Spring容器的核心,在这个方法中进行了bean的实例化、初始化、自动装配、AOP等功能。
* 下面先看看refresh()方法的代码,代码中加了部分个人的理解,简单介绍了每一行代码作用,后面会针对几个重要的方法做出详细分析
*
* 在refresh()方法中,比较重要的方法为
* ☆☆ invokeBeanFactoryPostProcessors(beanFactory)
* ☆☆ 和 finishBeanFactoryInitialization(beanFactory)。
* 其他的方法相对而言比较简单,下面主要分析这两个方法,
* 其他方法的作用,可以参考上面源码中的注释。
*
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 初始化属性配置文件、检验必须属性以及监听器
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 给beanFactory设置序列化id
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.0
// 向beanFactory中注册了两个BeanPostProcessor,以及三个和环境相关的bean
// 这两个后置处理器为 ApplicationContextAwareProcessor 和 ApplicationListenerDetector
// 前一个后置处理是为实现了ApplicationContextAware接口的类,回调setApplicationContext()方法,
// 后一个处理器时用来检测ApplicationListener类的,当某个Bean实现了ApplicationListener接口的bean被创建好后,会被加入到监听器列表中
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)
// 这个是给子类预留的一个阶段回调,方便子类在BeanFacotry准备完毕下一个阶段开始之前做一些拓展。
// 因此在AbstractApplicationContext中未作任何操作
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
★★★★★
执行所有的 BeanFactoryPostProcessor, 包括自定义的,以及spring内置的。默认情况下,容器中只有一个BeanFactoryPostProcessor,即:Spring内置的,ConfigurationClassPostProcessor(这个类很重要)
会先执行实现了BeanDefinitionRegistryPostProcessor接口的类,然后执行BeanFactoryPostProcessor的类
ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法进行了@Configuration类的解析,@ComponentScan的扫描,以及@Import注解的处理
经过这一步以后,会将所有交由spring管理的bean所对应的BeanDefinition放入到beanFactory的beanDefinitionMap中
同时ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法执行完后,向容器中添加了一个后置处理器————ImportAwareBeanPostProcessor
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/**
将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到:
注册所有的BeanPostProcessor,因为在方法里面调用了getBean()方法,所以在这一步,实际上已经将所有的BeanPostProcessor实例化了
为什么要在这一步就将BeanPostProcessor实例化呢?因为后面要实例化bean,而BeanPostProcessor是用来干预bean的创建过程的,所以必须在bean实例化之前就实例化所有的BeanPostProcessor(包括开发人员自己定义的)
最后再重新注册了ApplicationListenerDetector,这样做的目的是为了将ApplicationListenerDetector放入到后置处理器的最末端
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化MessageSource,用来做消息国际化。在一般项目中不会用到消息国际化
initMessageSource();
// Initialize event multicaster for this context.
/**
初始化事件广播器,如果容器中存在了名字为applicationEventMulticaster的广播器,则使用该广播器
如果没有,则初始化一个SimpleApplicationEventMulticaster
事件广播器的用途是,发布事件,并且为所发布的时间找到对应的事件监听器。
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 执行其他的初始化操作,例如和SpringMVC整合时,需要初始化一些其他的bean,但是对于纯spring工程来说,onFresh方法是一个空方法
// 改方法在AbstractApplicationContext层 未作任何实现,是为子类留的一个回调。方便子类在容器中基本准备完毕时进行拓展自己的行为。
// 比如web容器是在这里开始创建web服务对象,以及一些初始化工作。
onRefresh();
// Check for listener beans and register them.
/**
这一步会将自定义的listener的bean名称放入到事件广播器中
同时还会将早期的ApplicationEvent发布(对于单独的spring工程来说,在此时不会有任何ApplicationEvent发布,
但是和springMVC整合时,springMVC会执行onRefresh()方法,在这里会发布事件)
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
★★★★★
实例化剩余的非懒加载的单例bean(注意:剩余、非懒加载、单例)
为什么说是剩余呢?如果开发人员自定义了 BeanPosrProcessor,而 BeanPostProcessor在前面已经实例化了,
所以在这里不会再实例化,因此这里使用剩余一词
!!! (注意这里特意将对象的实例化和初始化过程分开了,因为在Spring创建Bean的过程中,是先将Bean通过反射创建对象,
然后通过后置处理器(BeanPostProcessor)来为对象的属性赋值。所以这里的实例化时指将Bean创建出来,初始化是指为bean的属性赋值)。
这一步是将无需懒加载的单例对象都进行初始化
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 结束refresh,主要干了一件事,就是发布一个事件ContextRefreshEvent,通知大家spring容器refresh结束了。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 出异常后销毁bean
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
// 在bean的实例化过程中,会缓存很多信息,例如bean的注解信息,但是当单例bean实例化完成后,这些缓存信息已经不会再使用了,所以可以释放这些内存资源了
resetCommonCaches();
}
}
}
重点部分:
// Initialize other special beans in specific context subclasses.
// 执行其他的初始化操作,例如和SpringMVC整合时,需要初始化一些其他的bean,但是对于纯spring工程来说,onFresh方法是一个空方法
// 改方法在AbstractApplicationContext层 未作任何实现,是为子类留的一个回调。方便子类在容器中基本准备完毕时进行拓展自己的行为。
// 比如web容器是在这里开始创建web服务对象,以及一些初始化工作。
onRefresh();
ServletWebServerApplicationContext的onRefresh方法
此类是 AbstractApplicationContext 子类
@Override
protected void onRefresh() {
super.onRefresh();
try {
//创建webserver
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
下面看一下 createWebServer() 方法:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
看一下ServletWebServerApplicationContext 类的 selfInitialize 方法:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
ServletWebServerApplicationContext里有一个实现的ServletContextInitializer逻辑:
ServletWebServerApplicationContext中,方法onRefresh()-->createWebServer()-->getSelfInitializer()-->selfInitialize()-->getServletContextInitializerBeans()-->new ServletContextInitializerBeans(getBeanFactory())。
ServletWebServerApplicationContext的onRefresh方法覆盖了AbstractApplicationContext的onRefresh方法,AbstractApplicationContext中,方法onRefresh被方法refresh调用。SpringApplication的run()-->refreshContext()-->refresh()-->AbstractApplicationContext的refresh()。
通过上面的分析可以看出,Springboot利用SpringFramework的特性,将DispatcherServlet、Filter或者Listener通过ServletContextInitializer的ServletContext,添加到tomcat之类的web容器中,这些都发生在Springboot启动的过程中。该接口的实现类不会被SpringServletContainerInitializer识别因此不会被Servlet容器自动执行。
ServletContextInitializers主要被Spring管理而不是Servlet容器。
getServletContextInitializerBeans()
逻辑就是从BeanFactory里获取指定类型的Bean列表,当然这其中就包含了一个DispatcherServletRegistrationBean
, 这个Bean的配置是在 DispatcherServletAutoConfiguration
里配置的
接下来重点看下DispatcherServletAutoConfiguration这个类的源码
这个类在Springboot-autoconfig包中(package org.springframework.boot.autoconfigure.web.servlet;)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
private final HttpProperties httpProperties;
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration(HttpProperties httpProperties,
WebMvcProperties webMvcProperties) {
this.httpProperties = httpProperties;
this.webMvcProperties = webMvcProperties;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setEnableLoggingRequestDetails(
this.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;
}
}
@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
private final WebMvcProperties webMvcProperties;
private final MultipartConfigElement multipartConfig;
public DispatcherServletRegistrationConfiguration(
WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
this.webMvcProperties = webMvcProperties;
this.multipartConfig = multipartConfigProvider.getIfAvailable();
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
dispatcherServlet, this.webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
}
@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));
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition
extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
if (!outcome.isMatch()) {
return outcome;
}
return checkServletRegistration(beanFactory);
}
private ConditionOutcome checkDefaultDispatcherName(
ConfigurableListableBeanFactory beanFactory) {
List<String> servlets = Arrays.asList(beanFactory
.getBeanNamesForType(DispatcherServlet.class, false, false));
boolean containsDispatcherBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (containsDispatcherBean
&& !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(startMessage().found("non dispatcher servlet")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
return ConditionOutcome.match();
}
private ConditionOutcome checkServletRegistration(
ConfigurableListableBeanFactory beanFactory) {
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays.asList(beanFactory
.getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty()) {
if (containsDispatcherRegistrationBean) {
return ConditionOutcome
.noMatch(message.found("non servlet registration bean").items(
DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome
.match(message.didNotFind("servlet registration bean").atAll());
}
if (registrations
.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
return ConditionOutcome.noMatch(message.found("servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
if (containsDispatcherRegistrationBean) {
return ConditionOutcome
.noMatch(message.found("non servlet registration bean").items(
DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.found("servlet registration beans")
.items(Style.QUOTE, registrations).append("and none is named "
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
private ConditionMessage.Builder startMessage() {
return ConditionMessage.forCondition("DispatcherServlet Registration");
}
}
}
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setEnableLoggingRequestDetails(
this.httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
-
先看类注解@Configuration,很明显这是一个典型的SpringBoot配置类。
-
@AutoConfigurerOrder来声明优先级。
-
@AutoConfigureAfter 或@AutoConfigureBefore, 从而进一步细化配置处理的顺序。
-
@ConditionalOnClass (DispatcherServlet.class)这个特殊的配置,能够确保我们的类路径下包含 DispatcherServlet。
-
@Conditional(DefaultDispatcherServletCondition.class)条件满足的情况下,ServletRegistrationBean 函 数才会启用,这有些复杂,但是能够检查在你的配置中,是否已经注册了分发器 Servlet
-
@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_ BEAN_NAME)条件的情况下,MultipartResolver 函数才会处于激活状态
更多推荐
所有评论(0)