Spring源碼閱讀(一)——淺析ApplicationContext

一滑进、IOC與DI

很多人學(xué)習(xí)Spring框架都是從IOC入手的, IOC(Inversion of Control)譯為“控制反轉(zhuǎn)”难礼,基于這一概念颗管,可以衍生出下面幾個(gè)問(wèn)題:

  1. 誰(shuí)控制了誰(shuí)?

  2. 控制了什么?

  3. 為什么是反轉(zhuǎn)重挑?

首先嗓化,我們來(lái)回答第一個(gè)問(wèn)題:傳統(tǒng)模式下,我們通常使用new來(lái)創(chuàng)建對(duì)象谬哀。而使用Spring刺覆,我們調(diào)用getBean(String name, Class<?> type)就可以直接獲得對(duì)象。因此史煎,IOC容器控制了對(duì)象谦屑。

那么,Spring容器控制了對(duì)象的什么呢篇梭?要回答這個(gè)問(wèn)題氢橙,我們可以直接把IOC的定義搬過(guò)來(lái):

所謂 IOC ,就是由 Spring IOC 容器來(lái)負(fù)責(zé)對(duì)象的生命周期和對(duì)象之間的關(guān)系恬偷。

為什么是反轉(zhuǎn)悍手?通過(guò)new來(lái)創(chuàng)建對(duì)象,對(duì)象的生命周期以及對(duì)象間的依賴都由程序員自己控制喉磁,這是正轉(zhuǎn)谓苟。而反轉(zhuǎn)剛好倒過(guò)來(lái),由容器來(lái)對(duì)對(duì)象進(jìn)行管理协怒,我們可以直接從容器中獲得對(duì)象及其依賴涝焙。

DI(Dependency Injection),即“依賴注入”孕暇,是指容器在運(yùn)行時(shí)決定組件之間的依賴仑撞,這是容器管理對(duì)象的另一個(gè)說(shuō)法。它并非是為系統(tǒng)添加新的功能妖滔,而是提升了組件的重用性隧哮。

二、ApplicationContext

也許大家從零零星星的文章中了解到座舍,ApplicationContext是一個(gè)高級(jí)的BeanFactory沮翔,它在BeanFactory的基礎(chǔ)上擴(kuò)展了很多功能。至于它有哪些功能曲秉,我們接下來(lái)細(xì)細(xì)道來(lái)采蚀。

ApplicationContext.png

從ApplicationContext的類圖中可以看到,它除了簡(jiǎn)接地繼承了BeanFactory之外承二,還繼承了MessageSource榆鼠、ResourceLoader、ApplicationEventPublisher和EnvironmentCapable接口亥鸠,言外之意是它包含了這幾個(gè)接口定義的所有功能妆够。

2.1 MessageSource

MessageSource是一個(gè)解析消息的策略接口识啦,它支持參數(shù)化與國(guó)際化。也許大家并不是很理解這句話神妹,下面我們一步步地解析這句話的含義颓哮。

/**
 * 用于解析消息的策略接口,支持此類消息的參數(shù)化和國(guó)際化
 */
public interface MessageSource {

    /**
     * 解析消息鸵荠,如果沒(méi)找到code對(duì)應(yīng)的消息就返回defaultMessage
     */
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

    /**
     * 解析消息题翻,如果沒(méi)找到code對(duì)應(yīng)的消息就拋異常
     
     */
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

    /**
     * 使用MessageSourceResolvable中的所有屬性解析消息
     * MessageSourceResolvable用code[]是啥意思呢?腰鬼??
     */
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

從MessageSource的源碼可以看到塑荒,它定義了三個(gè)getMessage方法熄赡,根據(jù)Locale指定的地區(qū)解析code對(duì)應(yīng)的消息,并用args參數(shù)替換消息中的占位符齿税,最終返回解析后的消息彼硫。

為了幫助大家理解,我舉個(gè)簡(jiǎn)單的例子:

  1. 在Spring配置文件spring.xml中定義properties文件的前綴
<bean id="messageSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>messages</value>
        </list>
    </property>
</bean>
  1. 新建幾個(gè)以messages為前綴的properties文件凌箕,內(nèi)容和文件名如下:
message=我只是個(gè){0}   #messages_zh_CN.properties
message=I am just a {0}  #messages_en.properties
  1. 解析并獲取解析后的消息
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
String message1=context.getMessage("message", new String[]{"小孩"}, Locale.CHINA);
String message2=context.getMessage("message", new String[]{"child"}, Locale.ENGLISH);
        
System.out.println("message1=" + message1);
System.out.println("message2=" + message2);
  1. 打印輸出
message1=我只是個(gè)小孩
message2=I am just a child

通過(guò)這個(gè)例子拧篮,大家應(yīng)該理解參數(shù)化和國(guó)際化的意思了吧~~
我們繼續(xù)回到ApplicationContext,它繼承了MessageSource接口牵舱,說(shuō)明它具有解析參數(shù)化和國(guó)際化消息的功能串绩。

2.2 BeanFactory

要實(shí)現(xiàn)IOC容器,BeanFactory是不可或缺的一環(huán)芜壁,它負(fù)責(zé)bean的創(chuàng)建和管理礁凡。從ApplicationContext的類圖中可以看出,ApplicationContext并不是直接繼承BeanFactory接口慧妄,而是繼承了BeanFactory的子接口:ListableBeanFactory和HierarchicalBeanFactory顷牌。

2.2.1 BeanFactory
/**
 * 創(chuàng)建、獲取bean以及一些bean相關(guān)的其他操作
 */
public interface BeanFactory {

    /**
     * FactoryBean的前綴
     */
    String FACTORY_BEAN_PREFIX = "&";

    /**
     * 返回name對(duì)應(yīng)的bean對(duì)象
     */
    Object getBean(String name) throws BeansException;

    /**
     * 返回指定type和name的bean對(duì)象
     */
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    /**
     * 返回現(xiàn)有對(duì)象或者用傳入的args創(chuàng)建對(duì)象塞淹,非原型模式傳入args會(huì)報(bào)錯(cuò)
     */
    Object getBean(String name, Object... args) throws BeansException;

    /**
     * 只能返回指定類型的唯一對(duì)象窟蓝,大于小于一個(gè)都報(bào)錯(cuò)
     */
    <T> T getBean(Class<T> requiredType) throws BeansException;

    /**
     * 返回requiredType對(duì)應(yīng)的bean對(duì)象或者用傳入的args創(chuàng)建對(duì)象,非原型模式傳入args會(huì)報(bào)錯(cuò)
     */
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    /**
     * 獲取requiredType類型的ObjectProvider
     */
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    /**
     * 獲取requiredType類型的ObjectProvider
     */
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    /**
     * 是否存在名字為name的bean
     */
    boolean containsBean(String name);

    /**
     * 名字為name的bean是否單例bean
     */
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    /**
     * 是否原型bean
     */
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    /**
     * 指定bean是否該類型
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * 指定bean是否該類型
     */
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * 獲取指定bean的類型
     */
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    /**
     * 獲取指定bean的別名
     */
    String[] getAliases(String name);
}

從BeanFactory的源碼中可以看出饱普,BeanFactory定義了一些對(duì)bean的基本操作运挫。繼承BeanFactory,ApplicationContext就有了對(duì)bean進(jìn)行操作的行為费彼。

2.2.2 ListableBeanFactory

上面BeanFactory定義的都是對(duì)單個(gè)bean進(jìn)行的操作滑臊,而ListableBeanFactory定義的操作大多數(shù)都是返回多個(gè)bean或者bean相關(guān)元素。從“Listable”就可以看出來(lái)箍铲,它定義了一些對(duì)bean進(jìn)行列表化的操作雇卷。因此,ApplicationContext也可以對(duì)bean進(jìn)行一些列表化操作。

/**
 * 可以枚舉所有bean實(shí)例关划,而不是按客戶端的請(qǐng)求逐個(gè)嘗試按名稱查找bean
 */
public interface ListableBeanFactory extends BeanFactory {

    /**
     * 是否存在指定name的BeanDefinition
     */
    boolean containsBeanDefinition(String beanName);

    /**
     * 獲取BeanFactory中BeanDefinition的數(shù)量
     */
    int getBeanDefinitionCount();

    /**
     * 獲取所有BeanDefinition的名字
     */
    String[] getBeanDefinitionNames();

    /**
     * 獲取指定類型的beanName數(shù)組
     */
    String[] getBeanNamesForType(ResolvableType type);

    /**
     * 獲取指定類型的beanName數(shù)組
     */
    String[] getBeanNamesForType(@Nullable Class<?> type);

    /**
     * 獲取指定類型的beanName數(shù)組小染,加了是否包含單例和非懶加載這幾個(gè)限制
     */
    String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);

    /**
     * 獲取指定類型的所有bean
     */
    <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;

    /**
     * 獲取指定類型的所有bean,加了是否包含單例和非懶加載這幾個(gè)限制
     */
    <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
            throws BeansException;

    /**
     * 獲取指定注解類型的beanNames
     */
    String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);

    /**
     * 獲取指定類型的bean對(duì)象
     */
    Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;

    /**
     * 根據(jù)beanName和指定注解類型獲取注解bean對(duì)象
     */
    @Nullable
    <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
            throws NoSuchBeanDefinitionException;
}
2.2.3 HierarchicalBeanFactory

HierarchicalBeanFactory定義的方法相對(duì)少點(diǎn)贮折,它定義了對(duì)BeanFactory分級(jí)的一些操作裤翩,比如返回父BeanFactory。

/**
 * 定義了對(duì)BeanFactory層次結(jié)構(gòu)的操作
 */
public interface HierarchicalBeanFactory extends BeanFactory {

    /**
     * Return the parent bean factory, or {@code null} if there is none.
     */
    @Nullable
    BeanFactory getParentBeanFactory();

    /**
     * 當(dāng)前context(不包含祖先context)是否含有指定name的bean
     */
    boolean containsLocalBean(String name);
}

2.3 ResourceLoader

ResourceLoader是一個(gè)資源加載的策略接口调榄,繼承這個(gè)接口說(shuō)明ApplicationContext有資源加載的功能踊赠,這也是Spring容器的第一步操作——加載配置文件。因?yàn)榕渲梦募x了bean以及bean之間的關(guān)系每庆,所以只有把配置文件加載進(jìn)來(lái)才能創(chuàng)建筐带、管理bean。

/**
 * 資源加載的策略接口
 */
public interface ResourceLoader {

    /** Pseudo URL prefix for loading from the class path: "classpath:". */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


    /**
     * 返回指定路徑的資源缤灵,這里只返回一個(gè)伦籍,說(shuō)明不支持模式匹配
     */
    Resource getResource(String location);

    /**
     * 返回統(tǒng)一的ClassLoader,而不是依賴于線程上下文ClassLoader
     */
    @Nullable
    ClassLoader getClassLoader();
}
2.3.1 ResourcePatternResolver

從名字就可以看出來(lái)腮出,ResourcePatternResolver對(duì)ResourceLoader進(jìn)行了擴(kuò)展帖鸦,它支持解析模式匹配的路徑。

/**
  * 解析模式匹配的路徑胚嘲,加載資源
  */
public interface ResourcePatternResolver extends ResourceLoader {

    /**
     * classpath url的前綴
     */
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    /**
     * 解析模式匹配的路徑作儿,返回多個(gè)Resource
     */
    Resource[] getResources(String locationPattern) throws IOException;

}

2.4 ApplicationEventPublisher

ApplicationEventPublisher定義了事件發(fā)布的功能,可以將事件發(fā)布給注冊(cè)了此應(yīng)用所有匹配的監(jiān)聽(tīng)器慢逾。由此可見(jiàn)立倍,ApplicationContet也可以發(fā)布事件。

/**
 * 定義了事件發(fā)布功能
 */
@FunctionalInterface
public interface ApplicationEventPublisher {

    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }

    /**
     * 通知在此應(yīng)用程序中注冊(cè)的所有匹配的listeners
     */
    void publishEvent(Object event);
}

2.5 EnvironmentCapable

EnvironmentCapable中只定義了getEnvironment的方法侣滩,向外界暴露了Environment接口口注。Environment是Spring運(yùn)行時(shí)的環(huán)境,它包含了profiles和properties君珠。
在一個(gè)軟件的開(kāi)發(fā)過(guò)程中寝志,我們往往要經(jīng)過(guò)很多步驟,而這些執(zhí)行步驟往往要將項(xiàng)目部署在不同的環(huán)境策添,比如測(cè)試時(shí)部署在測(cè)試環(huán)境材部、上線時(shí)部署在生產(chǎn)環(huán)境。profile正是起到了區(qū)分環(huán)境的作用唯竹,容器只會(huì)加載當(dāng)前active的profile環(huán)境所定義的bean及其他配置乐导。
properties是指當(dāng)前應(yīng)用的屬性,它可以在System環(huán)境變量浸颓、properties文件等多個(gè)地方進(jìn)行配置物臂。

三旺拉、小結(jié)

ApplicationContext是一個(gè)BeanFactory,它包含了創(chuàng)建bean以及其他管理bean的功能棵磷,除此之外它還有下列功能:

  1. 解析參數(shù)化和國(guó)際化消息蛾狗;
  2. 資源加載;
  3. 事件發(fā)布仪媒;
  4. 暴露環(huán)境沉桌。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市算吩,隨后出現(xiàn)的幾起案子留凭,更是在濱河造成了極大的恐慌,老刑警劉巖偎巢,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冰抢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡艘狭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門翠订,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巢音,“玉大人,你說(shuō)我怎么就攤上這事尽超」俸常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵似谁,是天一觀的道長(zhǎng)傲绣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)巩踏,這世上最難降的妖魔是什么秃诵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮塞琼,結(jié)果婚禮上菠净,老公的妹妹穿的比我還像新娘。我一直安慰自己彪杉,他們只是感情好毅往,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著派近,像睡著了一般攀唯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渴丸,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天侯嘀,我揣著相機(jī)與錄音另凌,去河邊找鬼。 笑死残拐,一個(gè)胖子當(dāng)著我的面吹牛途茫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溪食,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼囊卜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了错沃?” 一聲冷哼從身側(cè)響起栅组,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枢析,沒(méi)想到半個(gè)月后玉掸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡醒叁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年司浪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片把沼。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啊易,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饮睬,到底是詐尸還是另有隱情租谈,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布捆愁,位于F島的核電站割去,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏昼丑。R本人自食惡果不足惜呻逆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菩帝。 院中可真熱鬧页慷,春花似錦、人聲如沸胁附。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)控妻。三九已至州袒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弓候,已是汗流浹背郎哭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工他匪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夸研。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓邦蜜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亥至。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悼沈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容