Spring 源碼分析之AbstractApplicationContext源碼分析

首先我覺得分析ApplicationContext必須從它的實現類開始進行分析,AbstractApplicationContext我覺得是一個不錯的選擇,那我們就從這里開始逐一分析吧,首先我自己手畫了一張圖,作為索引吧,其中藍色的為類,紫色的為接口,箭頭 指向的方向是父類或者父接口。
在這里插入圖片描述
因為里面接口和方法過多,所以不做展示,下面具體來進行代碼分析。首先我們來看看這句話,MESSAGE_SOURCE_BEAN_NAME。

public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";

它這句話翻譯成中文就是消息資源的bean的一個name,我們暫時把它看成一個普通的beanName,我們來看看有哪些地方引用到了這個屬性,首先在initMessageSource方法里面有引用到,我把這些地方標紅顯示了。

protected void initMessageSource() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);// Make MessageSource aware of parent MessageSource.if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;if (hms.getParentMessageSource() == null) {// Only set parent context as parent MessageSource if no parent MessageSource// registered already.hms.setParentMessageSource(getInternalParentMessageSource());}}if (logger.isTraceEnabled()) {logger.trace("Using MessageSource [" + this.messageSource + "]");}}else {// Use empty MessageSource to be able to accept getMessage calls.DelegatingMessageSource dms = new DelegatingMessageSource();dms.setParentMessageSource(getInternalParentMessageSource());this.messageSource = dms;beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);if (logger.isTraceEnabled()) {logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");}}}

還有一個顯示的地方就是在StaticApplicationContext類中的構造器當中有使用到。下面是StaticApplicationContext的類結構圖:
在這里插入圖片描述

public StaticApplicationContext(@Nullable ApplicationContext parent) throws BeansException {super(parent);// Initialize and register a StaticMessageSource.this.staticMessageSource = new StaticMessageSource();getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.staticMessageSource);
}

我們下面再來看一下AbstractApplicationContext這個類的一些Fields,并且來理清一下對象和對象之間的依賴關系,首先是parent -ApplicationContext,我們找到是一個叫做getParent()的方法對這個私有的屬性進行了調用,然后又發現了getParentBeanFactory方法也對其進行了間接調用,因為BeanFactory是ApplicationContext的父接口,如下圖:
在這里插入圖片描述

private ApplicationContext parent;
public ApplicationContext getParent() {return this.parent;
}
public BeanFactory getParentBeanFactory() {return getParent();
}

在這個類中還有一個屬性是environment,這個environment在這里也有getter和setter方法,唯一需要注意的是如果調用getEnvironment()方法在environment為空的情況下會創建一個StandardEnvironment對象。

private ConfigurableEnvironment environment;
public ConfigurableEnvironment getEnvironment() {if (this.environment == null) {this.environment = createEnvironment();}return this.environment;
}

StandardEnvironment類繼承了抽象的AbstractEnvironment,它的類結構圖如下所示:
在這里插入圖片描述
還有一個比較重要的屬性就是beanFactoryPostProcessors,這事一個ArrayList的數組,Doc當中給出的解釋是當onRefresh的時候有用到。我找到了3個方法引用到了這個屬性,下面都已標紅。

private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {   Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");   this.beanFactoryPostProcessors.add(postProcessor);}
public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {   return this.beanFactoryPostProcessors;}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));   
}
}

往下面看還有一個屬性active,它是線程安全的,用到了CAS技術。它常常和closed這個屬性一起使用,我們發現,在如下3個地方有組合引用,我已用相應的顏色標識出來。

prepareRefresh
doClose
assertBeanFactoryActive

private final AtomicBoolean active = new AtomicBoolean();
private final AtomicBoolean closed = new AtomicBoolean();
protected void prepareRefresh() {   // Switch to active.   this.startupDate = System.currentTimeMillis();   this.closed.set(false);   this.active.set(true);   ....................   ....................}
protected void doClose() {   // Check whether an actual close attempt is necessary...   if (this.active.get() && this.closed.compareAndSet(false, true)) {      if (logger.isDebugEnabled()) {         logger.debug("Closing " + this);      }    ........................    ........................//Switch to inactive.   this.active.set(false);
}
protected void assertBeanFactoryActive() { if (!this.active.get()) {   if (this.closed.get()) {  throw new IllegalStateException(getDisplayName() + " has been closed already");    }      else {      throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");   } }}

我們繼續往下看,有一個startupShutdownMonitor的屬性,字面意思上面理解就是啟動關閉監視器,屬性在這個類當中的命名表示了它所發揮的作用,我們來看一下有哪些方法引用到了這個屬性。大家不知道發現沒有,還有一個“很像”的屬性在這里就是shutdownHook,這個和startupShutdownMonitor是配合在一起使用的。shudownHook在這里是一個線程類型的屬性。

private final Object startupShutdownMonitor = new Object();
private Thread shutdownHook;
public void refresh() throws BeansException, IllegalStateException {   synchronized (this.startupShutdownMonitor) {......
public void registerShutdownHook() {   if (this.shutdownHook == null) {      // No shutdown hook registered yet.      this.shutdownHook = new Thread() {         @Override         public void run() {            synchronized (startupShutdownMonitor) {               doClose();            }         }      };      Runtime.getRuntime().addShutdownHook(this.shutdownHook);   }}
public void close() {   synchronized (this.startupShutdownMonitor) {      doClose();      // If we registered a JVM shutdown hook, we don't need it anymore now:      // We've already explicitly closed the context.      if (this.shutdownHook != null) {         try {            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);         }         catch (IllegalStateException ex) {            // ignore - VM is already shutting down         }      }   }}

既然shutdownHook和startupShutdownMonitor一起使用,那么它們之間的關系我們得分析一下,hook顧名思義鉤子,說簡單點這個就是一個鉤子,也算是一個擴展點。我們來仔細分析一下它的幾個方法,首先是registerShutdownHook方法:這個方法有一句話特別重要,就是Runtime.getRuntime().addShutdownHook(this.shutdownHook);它實際上在系統層面上把鉤子線程添加到了JVM虛擬機。在鉤子運行的時候,就會執行doClose方法關閉并銷毀applicationContext。需要注意的一點是明白registerShutdownHook方法和close方法的不同點,在close方法中如果發現已經調用registerShutdownHook在JVM層面上注冊了鉤子,那么就調用Runtime.getRuntime().removeShutdownHook(this.shutdownHook)移除此鉤子,另外這個close的實現來自于closable接口的父接口AutoClosable接口方法,而registerShutdownHook則在PropertyResolver當中被定義。

public void <strong>registerShutdownHook</strong>() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread() {@Overridepublic void run() {synchronized (startupShutdownMonitor) {doClose();}}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}<br><br>
public void close() {synchronized (this.startupShutdownMonitor) {doClose();// If we registered a JVM shutdown hook, we don't need it anymore now:// We've already explicitly closed the context.if (this.shutdownHook != null) {try {Runtime.getRuntime().removeShutdownHook(this.shutdownHook);}catch (IllegalStateException ex) {// ignore - VM is already shutting down}}}
}

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

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

相關文章

[USACO15FEB]Superbull (最小生成樹)

題目鏈接 Solution 基本上就是個板子. 因為 \(n\) 很小,只有 \(2000\),所以直接暴力建圖,然后跑最小生成樹就好了. Code #include<bits/stdc.h> #define ll long long using namespace std; const int maxn2008; struct sj{int to,fr; ll w; }a[maxn*maxn]; int fa[maxn]…

Java中九大內置對象

1、Request對象 該對象封裝了用戶提交的信息&#xff0c;通過調用該對象相應的方法可以獲取封裝的信息&#xff0c;即使用該對象可以獲取用戶提交的信息。 當Request對象獲取客戶提交的漢字字符時&#xff0c;會出現亂碼問題&#xff0c;必須進行特殊處理。首先&#xff0c;…

ORACLE導出導入意外終止導致 ORACLE initialization or shutdown in progress 問題解決

由于意外情況導致 ORACLE initialization or shutdown in progress 個人理解為主要是歸檔日志出現問題&#xff0c; 首先cmd 1.sqlplus /nolog 進入sqlplus 2.connect /as sysdba 連接dba 3.shutdown normal 卸載數據庫 4.startup mount;重啟例程 5.alter database open;開…

Spring中資源的加載ResourceLoader

Spring中資源的加載是定義在ResourceLoader接口中的&#xff0c;它跟前面提到的抽象資源的關系如下&#xff1a; ResourceLoader的源碼 public interface ResourceLoader { /** Pseudo URL prefix for loading from the class path: "classpath:" */ String CLAS…

Codeforces Round #540 (Div. 3)(部分題解)

鏈接:http://codeforces.com/contest/1118 來源:Codeforces 文章目錄A. Water BuyingB. Tanya and Candies(前綴和)D1. Coffee and Coursework (Easy version)(貪心)D2. Coffee and Coursework (Hard Version)(二分)A. Water Buying 題意:用最小的花費買到剛好合適的東西.我們可…

集合一些方法陷阱

一&#xff1a;asList 數組轉ArrayList陷阱&#xff1a; asList() 源碼&#xff1a;public static <T> List<T> asList(T... a) { return new ArrayList<T>(a); } private final E[] a; ArrayList(E[] array) { if (arraynull) throw new NullPointerExcept…

java項目中的classpath

在java項目中&#xff0c;你一定碰到過classpath&#xff0c;通常情況下&#xff0c;我們是用它來指定配置/資源文件的路徑。在剛開始學習的時候&#xff0c;自己也糊里糊涂&#xff0c;但是現在&#xff0c;是時候弄清楚它到底是指什么了。 顧名思義&#xff0c;classpath就是…

C++命名空間(namespace)

在c中&#xff0c;名稱&#xff08;name&#xff09;可以是符號常量、變量、函數、結構、枚舉、類和對象等等。工程越大&#xff0c;名稱互相沖突性的可能性越大。另外使用多個廠商的類庫時&#xff0c;也可能導致名稱沖突。為了避免&#xff0c;在大規模程序的設計中&#xff…

P1556 幸福的路

題意&#xff1a;平面內有N頭牛$N\le 10$john從&#xff08;0,0&#xff09;出發&#xff0c;最后回到(0,0) 只有走到牛那里john才可以改變方向&#xff0c;否則沿著直線走 問john經過每一頭牛并且在每一頭牛出恰好改變方向一次的方案&#xff08;牛可以經過多次&#xff0c;但…

Class.getResource和ClassLoader.getResource

一案例驅動 二源碼分析 三類加載器ClassLoader 四總結 五參考 一案例驅動 最近加載文件的時候遇到了一個問題&#xff0c;很有意思&#xff01; 具體看下面案例代碼 public class TestClassLoader {public static void main(String[] args) {System.out.println(TestClassLoad…

spring-6、動態代理(cglib 與 JDK)

JDK動態代理與Cglib動態代理 JDK動態代理: 1.能夠繼承靜態代理的全部優點.并且能夠實現代碼的復用.2.動態代理可以處理一類業務.只要滿足條件 都可以通過代理對象進行處理.3.動態代理的靈活性不強.4.JDK 的動態代理要求代理者必須實現接口, , 否則不能生成代理對象. . 1 packag…

JDK安裝與配置(Windows 7系統)

1.前言 安裝之前需弄清JDK、JRE、JVM這幾個概念&#xff0c;不然稀里糊涂不知道自己在裝什么。 &#xff08;1&#xff09;什么是java環境&#xff1a;我們知道&#xff0c;想聽音樂就要安裝音樂播放器&#xff0c;想看圖片需要安裝圖片瀏覽器&#xff0c;同樣道理&#xff0c;…

UVA839

這道題又是一道遞歸的題目 先貼上代碼 //這種沒有明確說個數的動態分配還是得用new #include<cstdio> #include<iostream> using namespace std; struct mobile {int WL,DL,WR,DR;mobile *left,*right;mobile(mobile *aNULL,mobile*bNULL):left(a),right(b){} }; m…

Thread.getContextClassLoader與Thread.getClassLoader()區別

在閱讀spring boot啟動時候的源碼中&#xff0c;發現獲取classLoader使用的是getContextClassLoader于是乎產生了疑問&#xff0c;這種獲取ClassLoader的方式與我們最常見的通過Class.getClassLoader二者有什么區別&#xff1f;都是在什么場景下使用呢&#xff1f; 首先來看看…

ssl 的jks 生成工具

https://www.myssl.cn/tools/merge-jks-cert.html 通過key 私鑰 &#xff0c;和公鑰pem 生成jks 轉載于:https://www.cnblogs.com/vana/p/9594298.html

NOIP模擬賽10 題解

t3&#xff1a; 題意 給你一棵樹&#xff0c;然后每次兩種操作&#xff1a;1.給一個節點染色 &#xff1b; 2. 查詢一個節點與任意已染色節點 lca 的權值的最大值 分析 考慮一個節點被染色后的影響&#xff1a;令它的所有祖先節點&#xff08;包括自身&#xff09;的所有除去更…

洛谷 P1136 迎接儀式 解題報告

P1136 迎接儀式 題目描述 LHX教主要來X市指導OI學習工作了。為了迎接教主&#xff0c;在一條道路旁&#xff0c;一群Orz教主er穿著文化衫站在道路兩旁迎接教主&#xff0c;每件文化衫上都印著大字。一旁的Orzer依次擺出“歡迎歡迎歡迎歡迎……”的大字&#xff0c;但是領隊突然…

spring源碼分析-core.io包里面的類

前些日子看《深入理解javaweb開發》時&#xff0c;看到第一章java的io流&#xff0c;發覺自己對io流真的不是很熟悉。然后看了下JDK1.7中io包的一點點代碼&#xff0c;又看了org.springframework.core.io包的一些類和組織方式&#xff0c;當作是學習吧。總結一下。 先掛下spri…

對類Vue的MVVM前端庫的實現

關于實現MVVM&#xff0c;網上實在是太多了&#xff0c;本文為個人總結&#xff0c;結合源碼以及一些別人的實現 關于雙向綁定 vue 數據劫持 訂閱 - 發布ng 臟值檢查backbone.js 訂閱-發布(這個沒有使用過&#xff0c;并不是主流的用法)雙向綁定&#xff0c;從最基本的實現來說…

java.util.prefs.Preferences

我們經常需要將我們的程序中的設定&#xff0c;如窗口位置&#xff0c;開啟過的文件&#xff0c;用戶的選項設定等數據記錄下來&#xff0c;以做便用戶下一次開啟程序能繼續使用這些數據。 以前我們通常的做法是使用Properties類&#xff0c;它提供以下方法: void load(InputS…