Tomcat 9 源碼解析 -- 與SpringMvc如何結合在一起

 

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

 

  1. 先看類註解@Configuration,很明顯這是一個典型的SpringBoot配置類。

  2. @AutoConfigurerOrder來聲明優先級。

  3. @AutoConfigureAfter 或@AutoConfigureBefore, 從而進一步細化配置處理的順序。

  4. @ConditionalOnClass (DispatcherServlet.class)這個特殊的配置,能夠確保我們的類路徑下包含 DispatcherServlet。

  5. @Conditional(DefaultDispatcherServletCondition.class)條件滿足的情況下,ServletRegistrationBean 函 數纔會啓用,這有些複雜,但是能夠檢查在你的配置中,是否已經註冊了分發器 Servlet

  6. @ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_ BEAN_NAME)條件的情況下,MultipartResolver 函數纔會處於激活狀態

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章