前兩篇(Spring MVC原始碼——Root WebApplicationContext 和 Spring MVC原始碼——Servlet WebApplicationContext)講述了springmvc專案建立上下文的過程,這一篇帶大家瞭解一下springboot專案建立上下文的過程。
SpringApplication引導類
SpringApplication類用於啟動或者引導springboot專案,直接應用在java main方法中。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判斷當前web應用程式型別 this.webApplicationType = deduceWebApplicationType(); //找到*META-INF/spring.factories*中宣告的所有ApplicationContextInitializer的實現類並將其例項化 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //找到*META-INF/spring.factories*中宣告的所有ApplicationListener的實現類並將其例項化 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //獲得當前執行main方法的類物件 this.mainApplicationClass = deduceMainApplicationClass(); }
springboot專案WebApplicationType分為三種:非web型別,web型別(spring-mvc),響應式web型別(spring-webflux)
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework." + "web.reactive.DispatcherHandler"; private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework." + "web.servlet.DispatcherServlet"; private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
下面的run方法是springboot專案啟動的核心程式碼。
public ConfigurableApplicationContext run(String... args) { //開啟任務執行時間監聽器 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //設定系統屬性『java.awt.headless』,為true則啟用headless模式支援 configureHeadlessProperty(); //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*, //找到宣告的所有SpringApplicationRunListener的實現類並將其例項化, //之後逐個呼叫其started()方法,廣播SpringBoot要開始執行了。 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //建立並配置當前SpringBoot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile), //並遍歷呼叫所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準備完畢。 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //是否搜尋BeanInfo類 configureIgnoreBeanInfo(environment); //Banner列印 Banner printedBanner = printBanner(environment); //根據WebApplicationType的值來決定建立何種型別的ApplicationContext物件 context = createApplicationContext(); //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,獲取並例項化異常分析器 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //為ApplicationContext載入environment,之後逐個執行ApplicationContextInitializer的initialize()方法來進一步封裝ApplicationContext, //並呼叫所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個空的contextPrepared()方法】, //之後初始化IoC容器,並呼叫SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC載入完成, //這裡就包括通過**@EnableAutoConfiguration**匯入的各種自動配置類。 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //初始化所有自動配置類,呼叫ApplicationContext的refresh()方法 refreshContext(context); //空方法 afterRefresh(context, applicationArguments); /關閉任務執行時間監聽器 stopWatch.stop(); //如果開啟日誌,則列印執行是時間 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //呼叫所有的SpringApplicationRunListener的started()方法,廣播SpringBoot已經完成了ApplicationContext初始化的全部過程。 listeners.started(context); //遍歷所有註冊的ApplicationRunner和CommandLineRunner,並執行其run()方法。 //我們可以實現自己的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啟動過程進行擴充套件。 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //呼叫所有的SpringApplicationRunListener的running()方法,廣播SpringBoot已經可以處理服務請求了。 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
由上文可知,預設WebApplicationType是WebApplicationType.SERVLET,所以預設的上下文是AnnotationConfigServletWebServerApplicationContext。
//應用程式非web環境 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; //應用程式web環境 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; //應用程式響應式web環境 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; public enum WebApplicationType { //應用程式不需要任何應用伺服器 NONE, //應用程式內嵌web伺服器 SERVLET, //應用程式內嵌響應式web伺服器 REACTIVE } protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_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類結構層次如下。
父類ServletWebServerApplicationContext建立內嵌web應用伺服器如下。
@Override protected void onRefresh() { super.onRefresh(); try { //建立web應用服務 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //獲取ServletWebServerFactory型別的web伺服器工廠類,比如TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory 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(); }
Springmvc專案上下文和Springboot專案上下文淺析
Springmvc專案上下文
public WebApplicationContext initWebApplicationContext(ServletContext servletContext);
上下文建立好之後呼叫
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
其中 servletContext的例項是 org.apache.catalina.core.ApplicationContext;
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = org.springframework.web.context.WebApplicationContext.ROOT)
這樣rootcontext就建立好了,並且放入了servletContext中儲存。rootcontext獲取方式如下。
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
Springmvc專案中的DispatcherServlet是在xml中按照servlet格式配置的,這種方式建立的servlet例項沒有被spring容器管理。
/** WebApplicationContext for this servlet */ private WebApplicationContext webApplicationContext;
這種情況下webApplicationContext變數是無法注入的【DispatcherServlet例項沒有被spring容器管理】。看一下DispatcherServlet的父類FrameworkServlet是如何初始化上下文的,如下。
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //springmvc專案這塊的判斷為false;springboot專案為ture。 if (this.webApplicationContext != null) { ......
DispatcherServlet所屬上下文的儲存
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ... request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); ... }
DispatcherServlet所屬上下文獲取org.springframework.web.servlet.support.RequestContextUtils。
Springboot專案上下文
//ServletWebServerApplicationContext
protected void prepareWebApplicationContext(ServletContext servletContext) { ... servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); ... }
總結
經過三篇文章的分析,相信大家已經明白了Springmvc專案預設是有兩個上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,對應的型別是XmlServletWebServerApplicationContext),而Springboot專案預設是一個上下文,對應的型別是AnnotationConfigServletWebServerApplicationContext。如果有什麼疑問,請關注訂閱號,進行私聊。