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 函數纔會處於激活狀態