国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring IOC 容器源碼分析系列文章導讀

NSFish / 3146人閱讀

摘要:本文是容器源碼分析系列文章的第一篇文章,將會著重介紹的一些使用方法和特性,為后續的源碼分析文章做鋪墊。我們可以通過這兩個別名獲取到這個實例,比如下面的測試代碼測試結果如下本小節,我們來了解一下這個特性。

1. 簡介

Spring 是一個輕量級的企業級應用開發框架,于 2004 年由 Rod Johnson 發布了 1.0 版本。經過十幾年的迭代,現在的 Spring 框架已經非常成熟了。Spring 包含了眾多模塊,包括但不限于 Core、Bean、Context、AOP 和 Web 等。在今天,我們完全可以使用 Spring 所提供的一站式解決方案開發出我們所需要的應用。作為 Java 程序員,我們會經常和 Spring 框架打交道,所以還是很有必要弄懂 Spring 的原理。

本文是 Spring IOC 容器源碼分析系列文章的第一篇文章,將會著重介紹 Spring 的一些使用方法和特性,為后續的源碼分析文章做鋪墊。另外需要特別說明一下,本系列的源碼分析文章是基于Spring 4.3.17.RELEASE版本編寫的,而非最新的5.0.6.RELEASE版本。好了,關于簡介先說到這里,繼續來說說下面的內容。

2. 文章編排

寫 Spring IOC 這一塊的文章,挺讓我糾結的。我原本是打算在一篇文章中分析所有的源碼,但是后來發現文章實在太長。主要是因為 Spring IOC 部分的源碼實在太長,將這一部分的源碼貼在一篇文章中還是很壯觀的。當然估計大家也沒興趣讀下去,所以決定對文章進行拆分。這里先貼一張文章切分前的目錄結構:

如上圖,由目錄可以看出,假使在一篇文章中寫完所有內容,文章的長度將會非常長。所以在經過思考后,我會將文章拆分成一系列的文章,如下:

Spring IOC 容器源碼分析 - 獲取單例 bean - ?已更新

Spring IOC 容器源碼分析 - 創建單例 bean 的過程 - ?已更新

Spring IOC 容器源碼分析 - 創建原始 bean 對象 - ? 已更新

Spring IOC 容器源碼分析 - 循環依賴的解決辦法 - ? 已更新

Spring IOC 容器源碼分析 - 填充屬性到原始 bean 對象中 - ? 已更新

Spring IOC 容器源碼分析 - 余下的初始化工作 - ? 已更新

上面文章對應的源碼分析工作均已經完成,所有的文章將會在近期內進行更新。

3. Spring 模塊結構

Spring 是分模塊開發的,Spring 包含了很多模塊,其中最為核心的是 bean 容器相關模塊。像 AOP、MVC、Data 等模塊都要依賴 bean 容器。這里先看一下 Spring 框架的結構圖:

圖片來源:Spring 官方文檔

從上圖中可以看出Core Container處于整個框架的最底層(忽略 Test 模塊),在其之上有 AOP、Data、Web 等模塊。既然 Spring 容器是最核心的部分,那么大家如果要讀 Spring 的源碼,容器部分必須先弄懂。本篇文章作為 Spring IOC 容器的開篇文章,就來簡單介紹一下容器方面的知識。請繼續往下看。

4. Spring IOC 部分特性介紹

本章將會介紹 IOC 中的部分特性,這些特性均會在后面的源碼分析中悉數到場。如果大家不是很熟悉這些特性,這里可以看一下。

4.1 alias

alias 的中文意思是“別名”,在 Spring 中,我們可以使用 alias 標簽給 bean 起個別名。比如下面的配置:


    


這里我們給hello這個 beanName 起了一個別名alias-hello,然后又給別名alias-hello起了一個別名double-alias-hello。我們可以通過這兩個別名獲取到hello這個 bean 實例,比如下面的測試代碼:

public class ApplicationContextTest {

    @Test
    public void testAlias() {
        String configLocation = "application-alias.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
        System.out.println("    alias-hello -> " + applicationContext.getBean("alias-hello"));
        System.out.println("double-alias-hello -> " + applicationContext.getBean("double-alias-hello"));
    }
}

測試結果如下:

4.2 autowire

本小節,我們來了解一下 autowire 這個特性。autowire 即自動注入的意思,通過使用 autowire 特性,我們就不用再顯示的配置 bean 之間的依賴了。把依賴的發現和注入都交給 Spring 去處理,省時又省力。autowire 幾個可選項,比如 byName、byType 和 constructor 等。autowire 是一個常用特性,相信大家都比較熟悉了,所以本節我們就 byName 為例,快速結束 autowire 特性的介紹。

當 bean 配置中的 autowire = byName 時,Spring 會首先通過反射獲取該 bean 所依賴 bean 的名字(beanName),然后再通過調用 BeanFactory.getName(beanName) 方法即可獲取對應的依賴實例。autowire = byName 原理大致就是這樣,接下來我們來演示一下。

public class Service {

    private Dao mysqlDao;

    private Dao mongoDao;

    // 忽略 getter/setter

    @Override
    public String toString() {
        return super.toString() + "
					{" +
            "mysqlDao=" + mysqlDao +
            ", mongoDao=" + mongoDao +
            "}";
    }
}

public interface Dao {}
public class MySqlDao implements Dao {}
public class MongoDao implements Dao {}

配置如下:






    
    



測試代碼如下:

String configLocation = "application-autowire.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("service-without-autowire -> " + applicationContext.getBean("service-without-autowire"));
System.out.println("service-with-autowire -> " + applicationContext.getBean("service-with-autowire"));

測試結果如下:

從測試結果可以看出,兩種方式配置方式都能完成解決 bean 之間的依賴問題。只不過使用 autowire 會更加省力一些,配置文件也不會冗長。這里舉的例子比較簡單,假使一個 bean 依賴了十幾二十個 bean,再手動去配置,恐怕就很難受了。

4.3 FactoryBean

FactoryBean?看起來是不是很像 BeanFactory 孿生兄弟。不錯,他們看起來很像,但是他們是不一樣的。FactoryBean 是一種工廠 bean,與普通的 bean 不一樣,FactoryBean 是一種可以產生 bean 的 bean,好吧說起來很繞嘴。FactoryBean 是一個接口,我們可以實現這個接口。下面演示一下:

public class HelloFactoryBean implements FactoryBean {

    @Override
    public Hello getObject() throws Exception {
        Hello hello = new Hello();
        hello.setContent("hello");
        return hello;
    }

    @Override
    public Class getObjectType() {
        return Hello.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

配置如下:

測試代碼如下:

public class ApplicationContextTest {

    @Test
    public void testFactoryBean() {
        String configLocation = "application-factory-bean.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
        System.out.println("helloFactory -> " + applicationContext.getBean("helloFactory"));
        System.out.println("&helloFactory -> " + applicationContext.getBean("&helloFactory"));
    }
}

測試結果如下:

由測試結果可以看到,當我們調用 getBean("helloFactory") 時,ApplicationContext 會返回一個 Hello 對象,該對象是 HelloFactoryBean 的 getObject 方法所創建的。如果我們想獲取 HelloFactoryBean 本身,則可以在 helloFactory 前加上一個前綴&,即&helloFactory

4.4 factory-method

介紹完 FactoryBean,本節再來看看了一個和工廠相關的特性 -- factory-method。factory-method 可用于標識靜態工廠的工廠方法(工廠方法是靜態的),直接舉例說明吧:

public class StaticHelloFactory {

    public static Hello getHello() {
        Hello hello = new Hello();
        hello.setContent("created by StaticHelloFactory");
        return hello;
    }
}

配置如下:

測試代碼如下:

public class ApplicationContextTest {

    @Test
    public void testFactoryMethod() {
        String configLocation = "application-factory-method.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
        System.out.println("staticHelloFactory -> " + applicationContext.getBean("staticHelloFactory"));
    }
}

測試結果如下:

對于非靜態工廠,需要使用 factory-bean 和 factory-method 兩個屬性配合。關于 factory-bean 這里就不繼續說了,留給大家自己去探索吧。

4.5 lookup-method

lookup-method 特性可能大家用的不多(我也沒用過),不過它也是個有用的特性。在介紹這個特性前,先介紹一下背景。我們通過 BeanFactory getBean 方法獲取 bean 實例時,對于 singleton 類型的 bean,BeanFactory 每次返回的都是同一個 bean。對于 prototype 類型的 bean,BeanFactory 則會返回一個新的 bean。現在考慮這樣一種情況,一個 singleton 類型的 bean 中有一個 prototype 類型的成員變量。BeanFactory 在實例化 singleton 類型的 bean 時,會向其注入一個 prototype 類型的實例。但是 singleton 類型的 bean 只會實例化一次,那么它內部的 prototype 類型的成員變量也就不會再被改變。但如果我們每次從 singleton bean 中獲取這個 prototype 成員變量時,都想獲取一個新的對象。這個時候怎么辦?舉個例子(該例子源于《Spring 揭秘》一書),我們有一個新聞提供類(NewsProvider),這個類中有一個新聞類(News)成員變量。我們每次調用 getNews 方法都想獲取一條新的新聞。這里我們有兩種方式實現這個需求,一種方式是讓 NewsProvider 類實現 ApplicationContextAware 接口(實現 BeanFactoryAware 接口也是可以的),每次調用 NewsProvider 的 getNews 方法時,都從 ApplicationContext 中獲取一個新的 News 實例,返回給調用者。第二種方式就是這里的 lookup-method 了,Spring 會在運行時對 NewsProvider 進行增強,使其 getNews 可以每次都返回一個新的實例。說完了背景和解決方案,接下來就來寫點測試代碼驗證一下。

在演示兩種處理方式前,我們先來看看不使用任何處理方式,BeanFactory 所返回的 bean 實例情況。相關類定義如下:

public class News {
    // 僅演示使用,News 類中無成員變量
}

public class NewsProvider {

    private News news;

    public News getNews() {
        return news;
    }

    public void setNews(News news) {
        this.news = news;
    }
}

配置信息如下:



    

測試代碼如下:

String configLocation = "application-lookup-method.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
NewsProvider newsProvider = (NewsProvider) applicationContext.getBean("newsProvider");
System.out.println(newsProvider.getNews());
System.out.println(newsProvider.getNews());

測試結果如下:

從測試結果中可以看出,newsProvider.getNews() 方法兩次返回的結果都是一樣的,這個是不滿足要求的。

4.5.1 實現 ApplicationContextAware 接口

我們讓 NewsProvider 實現 ApplicationContextAware 接口,實現代碼如下:

public class NewsProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    private News news;

    /** 每次都從 applicationContext 中獲取一個新的 bean */
    public News getNews() {
        return applicationContext.getBean("news", News.class);
    }

    public void setNews(News news) {
        this.news = news;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

配置和測試代碼同上,測試結果如下:

這里兩次獲取的 news 并就不是同一個 bean 了,滿足了我們的需求。

4.5.2 使用 lookup-method 特性

使用 lookup-method 特性,配置文件需要改一下。如下:



    

NewsProvider 的代碼沿用 4.5.1 小節之前貼的代碼。測試代碼稍微變一下,如下:

String configLocation = "application-lookup-method.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
NewsProvider newsProvider = (NewsProvider) applicationContext.getBean("newsProvider");
System.out.println("newsProvider -> " + newsProvider);
System.out.println("news 1 -> " + newsProvider.getNews());
System.out.println("news 2 -> " + newsProvider.getNews());

測試結果如下:

從上面的結果可以看出,new1 和 new2 指向了不同的對象。同時,大家注意看 newsProvider,似乎變的很復雜。由此可看出,NewsProvider 被 CGLIB 增強了。

4.6 depends-on

當一個 bean 直接依賴另一個 bean,可以使用 標簽進行配置。不過如某個 bean 并不直接依賴于 其他 bean,但又需要其他 bean 先實例化好,這個時候就需要使用 depends-on 特性了。depends-on 特性比較簡單,就不演示了。僅貼一下配置文件的內容,如下:

這里有兩個簡單的類,其中 Hello 需要 World 在其之前完成實例化。相關配置如下:


4.7 BeanPostProcessor

BeanPostProcessor 是 bean 實例化時的后置處理器,包含兩個方法,其源碼如下:

public interface BeanPostProcessor {
    // bean 初始化前的回調方法
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    // bean 初始化后的回調方法    
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

BeanPostProcessor 是 Spring 框架的一個擴展點,通過實現 BeanPostProcessor 接口,我們就可插手 bean 實例化的過程。比如大家熟悉的 AOP 就是在 bean 實例后期間將切面邏輯織入 bean 實例中的,AOP 也正是通過 BeanPostProcessor 和 IOC 容器建立起了聯系。這里我來演示一下 BeanPostProcessor 的使用方式,如下:

/**
 * 日志后置處理器,將會在 bean 創建前、后打印日志
 */
public class LoggerBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before " + beanName + " Initialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After " + beanName + " Initialization");
        return bean;
    }
}

配置如下:


    

測試代碼如下:

public class ApplicationContextTest {

    @Test
    public void testBeanPostProcessor() {
        String configLocation = "application-bean-post-processor.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
    }
}

測試結果如下:

與 BeanPostProcessor 類似的還有一個叫 BeanFactoryPostProcessor 拓展點,顧名思義,用戶可以通過這個拓展點插手容器啟動的過程。不過這個不屬于本系列文章范疇,暫時先不細說了。

4.8 BeanFactoryAware

Spring 中定義了一些列的 Aware 接口,比如這里的 BeanFactoryAware,以及 BeanNameAware 和 BeanClassLoaderAware 等等。通過實現這些 Aware 接口,我們可以在運行時獲取一些配置信息或者其他一些信息。比如實現 BeanNameAware 接口,我們可以獲取 bean 的配置名稱(beanName)。通過實現 BeanFactoryAware 接口,我們可以在運行時獲取 BeanFactory 實例。關于 Aware 類型接口的使用,可以參考4.5.1 實現 ApplicationContextAware 接口一節中的敘述,這里就不演示了。

5. 閱讀源碼的一些建議

我在去年八月份的時候,嘗試過閱讀 Spring 源碼。不過 Spring 源碼太多了,調用復雜,看著看著就迷失在了代碼的海洋里。不過好在當時找到了一個經過簡化后的類 Spring 框架,該框架黃億華前輩在學習 Spring 源碼時所寫的 tiny-spring。如果大家覺得看 Spring 源碼比較困難,可以先學習一下 tiny-spring 的源碼,先對 Spring 代碼結構有個大概的了解。

另外也建議大家自己動手實現一個簡單的 IOC 容器,通過實踐,才會有更多的感悟。我在去年八月份的時候,實現過一個簡單的 IOC 和 AOP(可以參考我去年發的文章:仿照 Spring 實現簡單的 IOC 和 AOP - 上篇),并在最后將兩者整合到了一起。正是有了之前的實踐,才使得我對 Spring 的原理有了更進一步的認識。當做完一些準備工作后,再次閱讀 Spring 源碼,就沒以前那么痛苦了。當然,Spring 的代碼經過十幾年的迭代,代碼量很大。我在分析的過程中也只是盡量保證搞懂重要的邏輯,無法做到面面俱到。不過,如果大家愿意去讀 Spring 源碼,我相信會比我理解的更透徹。

除了上面說的動手實踐外,在閱讀源碼的過程中,如果實在看不懂,不妨調試一下。比如某個變量你不知道有什么用,但是它又比較關鍵,在多個地方都出現了,顯然你必須要搞懂它。那么此時寫點測試代碼調試一下,立馬就能知道它有什么用了。

以上是我在閱讀源碼時所使用過的一些方法,當然僅有上面那些可能還不夠。本節的最后再推薦兩本書,如下:

《Spring 揭秘》- 王福強著

《Spring源碼深度解析》- 郝佳著

第二本書在豆瓣上的評分不太好,不過我覺得還好。這本書我僅看了容器部分的代碼分析,總的來說還行。我在看循環依賴那一塊的代碼時,這本書還是給了我一些幫助的。好了,本節就到這里。

6. 寫在最后

在本文的最后一章,我來說說我為什么閱讀 Spring 的源碼吧。對我個人而言,有兩個原因。第一,作為 Java Web 開發人員,我們基本上繞不過 Spring 技術棧。當然除了 Spring,還有很多其他的選擇(比如有些公司自己封裝框架)。但不可否認,Spring 現在仍是主流。對于這樣一個經常打交道的框架,弄懂實現原理,還有很有必要的。起碼在它出錯輸出一大堆異常時,你不會很心慌,可以從容的 debug。第二,我去年的這個時候,工作差不多快滿一年。我在寫第一年的工作總結時,覺得很心慌。感覺第一年好像只學到了一點開發的皮毛,技術方面,沒什么積累。加上后來想換工作,心里想著就自己現在的水平恐怕要失業了。所以為了平復自己焦慮的情緒,于是在去年的八月份的時候,我開始寫博客了。到現在寫了近30篇博客(比較少),其中有兩篇文章被博客平臺作為優秀文章推薦過。現在,又快到7月份了,工齡馬上又要+1了。所以我現在在抓緊寫 Spring 相關的文章,希望在六月份多寫幾篇。算是對自己工作滿兩年的一個階段性技術總結,也是一個紀念吧。

當然,看懂源碼并不是什么很了不起的事情,畢竟寫這些源碼的人才是真正的大神。我在大學的時候,自學 MFC(沒錯,就是微軟早已淘汰的東西)。并讀過侯捷老師著的《深入淺出MFC》一書,這本書中的一句話對我影響至深 - “勿在浮沙筑高臺”。勿在浮沙筑高臺,對于一個程序員來說,基礎很重要。所以我現在也在不斷的學習,希望能把基礎打好,這樣以后才能進入更高的層次。

好了,感謝大家耐心看完我的嘮叨。本文先到這里,我要去寫后續的文章了,后面再見。bye~

附錄:Spring 源碼分析文章列表 Ⅰ. IOC
更新時間 標題
2018-05-30 Spring IOC 容器源碼分析系列文章導讀
2018-06-01 Spring IOC 容器源碼分析 - 獲取單例 bean
2018-06-04 Spring IOC 容器源碼分析 - 創建單例 bean 的過程
2018-06-06 Spring IOC 容器源碼分析 - 創建原始 bean 對象
2018-06-08 Spring IOC 容器源碼分析 - 循環依賴的解決辦法
2018-06-11 Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象
2018-06-11 Spring IOC 容器源碼分析 - 余下的初始化工作
Ⅱ. AOP
更新時間 標題
2018-06-17 Spring AOP 源碼分析系列文章導讀
2018-06-20 Spring AOP 源碼分析 - 篩選合適的通知器
2018-06-20 Spring AOP 源碼分析 - 創建代理對象
2018-06-22 Spring AOP 源碼分析 - 攔截器鏈的執行過程
Ⅲ. MVC
更新時間 標題
2018-06-29 Spring MVC 原理探秘 - 一個請求的旅行過程
2018-06-30 Spring MVC 原理探秘 - 容器的創建過程
本文在知識共享許可協議 4.0 下發布,轉載需在明顯位置處注明出處
作者:coolblog.xyz
本文同步發布在我的個人博客:http://www.coolblog.xyz


本作品采用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69587.html

相關文章

  • Spring IOC 容器源碼分析 - 余下的初始化工作

    摘要:簡介本篇文章是容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對象是方法,該方法用于對已完成屬性填充的做最后的初始化工作。后置處理器是拓展點之一,通過實現后置處理器接口,我們就可以插手的初始化過程。 1. 簡介 本篇文章是Spring IOC 容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對象是 initializeBean 方法,該方法用于對已完成屬性填充的 bean 做最...

    Alfred 評論0 收藏0
  • Spring AOP 源碼分析系列文章導讀

    摘要:在寫完容器源碼分析系列文章中的最后一篇后,沒敢懈怠,趁熱打鐵,花了天時間閱讀了方面的源碼。從今天開始,我將對部分的源碼分析系列文章進行更新。全稱是,即面向切面的編程,是一種開發理念。在中,切面只是一個概念,并沒有一個具體的接口或類與此對應。 1. 簡介 前一段時間,我學習了 Spring IOC 容器方面的源碼,并寫了數篇文章對此進行講解。在寫完 Spring IOC 容器源碼分析系列...

    張春雷 評論0 收藏0
  • Spring IOC 容器源碼分析 - 獲取單例 bean

    摘要:簡介為了寫容器源碼分析系列的文章,我特地寫了一篇容器的導讀文章。在做完必要的準備工作后,從本文開始,正式開始進入源碼分析的階段。從緩存中獲取單例。返回以上就是和兩個方法的分析。 1. 簡介 為了寫 Spring IOC 容器源碼分析系列的文章,我特地寫了一篇 Spring IOC 容器的導讀文章。在導讀一文中,我介紹了 Spring 的一些特性以及閱讀 Spring 源碼的一些建議。在...

    lufficc 評論0 收藏0
  • Spring IOC 容器源碼分析 - 創建單例 bean 的過程

    摘要:關于創建實例的過程,我將會分幾篇文章進行分析。源碼分析創建實例的入口在正式分析方法前,我們先來看看方法是在哪里被調用的。時,表明方法不存在,此時拋出異常。該變量用于表示是否提前暴露單例,用于解決循環依賴。 1. 簡介 在上一篇文章中,我比較詳細的分析了獲取 bean 的方法,也就是getBean(String)的實現邏輯。對于已實例化好的單例 bean,getBean(String) ...

    mochixuan 評論0 收藏0
  • Spring IOC 容器源碼分析 - 循環依賴的解決辦法

    摘要:實例化時,發現又依賴于。一些緩存的介紹在進行源碼分析前,我們先來看一組緩存的定義。可是看完源碼后,我們似乎仍然不知道這些源碼是如何解決循環依賴問題的。 1. 簡介 本文,我們來看一下 Spring 是如何解決循環依賴問題的。在本篇文章中,我會首先向大家介紹一下什么是循環依賴。然后,進入源碼分析階段。為了更好的說明 Spring 解決循環依賴的辦法,我將會從獲取 bean 的方法getB...

    aikin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<