Springboot源碼——應用程序上下文分析

  前兩篇(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,UndertowServletWebServerFactoryServletWebServerFactory 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項目上下文

Springmvc項目的rootcontext的創建時通過 xml中 配置的org.springframework.web.context.ContextLoaderListener,其父類ContextLoader中有一個初始化上下文的方法,如下。
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容器管理。

DispatcherServlet實現了ApplicationContextAware接口,有一個成員變量來保存此servlet對應的上下文,如下。
/** 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項目上下文

Springboot項目中,調試代碼時發現DispatcherServlet的父類FrameworkServlet在初始化上下文的時候rootcontext 和?DispatcherServlet成員變量webApplicationContext保存的是一個實例,即AnnotationConfigServletWebServerApplicationContext實例。
上面也提到了DispatcherServlet【對應的實例被spring容器管理】實現了ApplicationContextAware接口,webApplicationContext保存的上下文是通過自動注入而來。
RootContext(AnnotationConfigServletWebServerApplicationContext)保存到servletcontext中的操作,如下。
//ServletWebServerApplicationContext
protected
void prepareWebApplicationContext(ServletContext servletContext) {...servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);... }

總結

經過三篇文章的分析,相信大家已經明白了Springmvc項目默認是有兩個上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,對應的類型是XmlServletWebServerApplicationContext),而Springboot項目默認是一個上下文,對應的類型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑問,請關注訂閱號,進行私聊。

轉載于:https://www.cnblogs.com/hujunzheng/p/10854464.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531194.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531194.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531194.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于zookeeper實現分布式配置中心(一)

最近在學習zookeeper&#xff0c;發現zk真的是一個優秀的中間件。在分布式環境下&#xff0c;可以高效解決數據管理問題。在學習的過程中&#xff0c;要深入zk的工作原理&#xff0c;并根據其特性做一些簡單的分布式環境下數據管理工具。本文首先對zk的工作原理和相關概念做一下…

基于zookeeper實現分布式配置中心(二)

上一篇&#xff08;基于zookeeper實現分布式配置中心&#xff08;一&#xff09;&#xff09;講述了zookeeper相關概念和工作原理。接下來根據zookeeper的特性&#xff0c;簡單實現一個分布式配置中心。 配置中心的優勢 1、各環境配置集中管理。 2、配置更改&#xff0c;實時推…

Redis分布式鎖實戰

背景 目前開發過程中&#xff0c;按照公司規范&#xff0c;需要依賴框架中的緩存組件。不得不說&#xff0c;做組件的大牛對CRUD操作的封裝&#xff0c;連接池、緩存路由、緩存安全性的管控都處理的無可挑剔。但是有一個小問題&#xff0c;該組件沒有對分布式鎖做實現&#xff…

基于RobotFramework實現自動化測試

Java robotframework seleniumlibrary 使用Robot Framework Maven Plugin&#xff08;http://robotframework.org/MavenPlugin/&#xff09;執行自動化測試chromedriver下載&#xff1a; http://chromedriver.storage.googleapis.com/index.htmlchromedriver和chrome版本對應…

Springboot國際化信息(i18n)解析

國際化信息理解 國際化信息也稱為本地化信息 。 Java 通過 java.util.Locale 類來表示本地化對象&#xff0c;它通過 “語言類型” 和 “國家/地區” 來創建一個確定的本地化對象 。舉個例子吧&#xff0c;比如在發送一個具體的請求的時候&#xff0c;在header中設置一個鍵值對…

看了就知道為什么別人C語言學習效率那么高了

談及C語言&#xff0c;我想C語言功能強大都應該知道、應用廣泛&#xff0c;一旦掌握了后&#xff0c;你就可以理直氣壯地對他人說“我是電腦高手&#xff01;”&#xff0c;而且以后若是再自學其他語言就顯得輕而易舉了。憂慮的是&#xff0c;C語言般博大精深&#xff0c;太難學…

C語言一看就能上手的干貨!你確定你不來看嗎?

本地環境設置 如果您想要設置 C 語言環境&#xff0c;您需要確保電腦上有以下兩款可用的軟件&#xff0c;文本編輯器和 C 編譯器。 文本編輯器 這將用于輸入您的程序。文本編輯器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。文本編輯器的名稱…

C語言爆炸干貨,小白你還不來看看嘛!

①&#xff1a;數據類型 int(整型)&#xff0c;short int(短整型)&#xff0c;long int(長整型)&#xff0c; char(字符型)&#xff0c;float&#xff08;單精度浮點型&#xff09; double&#xff08;雙精度浮點型&#xff09; C語言編程交流群815393895 ②&#xff1a;邏…

10萬碼農五年的C語言筆記!你現在知道別人為什么這么優秀了嗎?

c語言對許多同學來說確實是一門比較難學的課程&#xff0c;不僅抽象&#xff0c;而且繁瑣&#xff0c;但這又是一門不得不學的課程。前兩節可能還有興致聽一聽&#xff0c;然而&#xff0c;再過幾節課就是一臉蒙比。憑空要想出一道題的算法和程序&#xff0c;根本無從下手。 所…

C語言從來都沒有過時,你大爺終究是你大爺

直到今天&#xff0c;有人在喊C語言過時的語言&#xff0c;還有什么值得學習的&#xff0c;現在看Python&#xff0c;PHP等語言現在都很容易用&#xff0c;誰還在學習老C語言&#xff0c;其實這是真的嗎&#xff1f;作者下載了兩種語言的源代碼作為下載器。由于空間的限制&…

C語言超級瑪麗菜單模塊源碼

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和…

C語言使用函數必須知道的3點注意事項!

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得…

C語言/C++編程學習:C語言環境設置!

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得…

C語言指針原來也可以這么的通俗易懂!

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得…

C語言過時了?你在做夢?

為什么要使用C語言&#xff1f; 在過去的四十年里&#xff0c;C語言已經成為世界上最流行、最重要的一種編程語言。 C是一種融合了控制特性的現代語言&#xff0c;而我們已發現在計算機科學的理論和實踐中&#xff0c;控制特性是很重要的。其設計使得用戶可以自然地采用自頂向…

C/C++的轉義字符詳解

所有的ASCII碼都可以用“\”加數字&#xff08;一般是8進制數字&#xff09;來表示。而C中定義了一些字母前加"\"來表示常見的那些不能顯示的ASCII字符&#xff0c;如\0,\t,\n等&#xff0c;就稱為轉義字符&#xff0c;因為后面的字符&#xff0c;都不是它本來的ASCI…

C語言深入理解!助你向大佬邁進!

Dennis Ritchie 過世了&#xff0c;他發明了C語言&#xff0c;一個影響深遠并徹底改變世界的計算機語言。一門經歷40多年的到今天還長盛不衰的語言&#xff0c;今天很多語言都受到C的影響&#xff0c;C&#xff0c;Java&#xff0c;C#&#xff0c;Perl&#xff0c; PHP&#xf…

【初涉C語言】程序員歡迎來到C語言的世界!

計算機發展史 機器語言所有的代碼里面只有0和1優點&#xff1a;直接對硬件產生作用&#xff0c;程序的執行效率非常高缺點&#xff1a;指令又多又難記、可讀性差、無可移植性匯編語言符號化的機器語言&#xff0c;用一個符號&#xff08;英文單詞、數字&#xff09;來代表一條…

C語言和C++的區別整理詳解!

c和c主要區別 根據書中的描述&#xff0c;進行了整理 推薦一個我自己的C/C交流裙815393895 1、 源代碼文件的擴展名 摘自1.4.1 C實現源代碼文件的擴展名UNIXC、cc、cxx、cGNU CC、cc、cxx、cpp、cDigital Marscpp、cxxBorland CcppWatcomcppMicrosoft Visual Ccpp、cxx、cc…

揭示C語言函數調用的本質解析

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得到…