BeanFactory和ApplicationContext

概述

BeanFatory作為Spring的IOC容器主要提供了注冊(cè)Bean和管理依賴(lài)這兩個(gè)功能缀蹄,它的接口包括如下函數(shù):

BeanFactory.png

ApplicationContext在間接繼承了BeanFactory的基礎(chǔ)上榆骚,還多繼承了四個(gè)接口,提供了更多的功能框冀,如事件發(fā)布阅爽,資源載入,消息等。
BeanFactory and ApplicationContext

實(shí)現(xiàn)

BeanFactory接口的實(shí)現(xiàn)為了實(shí)現(xiàn)注冊(cè)Bean的功能钥飞,需要使用BeanDefinitionRegistry這個(gè)接口,這三者的關(guān)系如圖:

BeanFactoryAndBeanDefinitionRegistry.png

可以看到衫嵌,BeanFactory的實(shí)現(xiàn)同時(shí)實(shí)現(xiàn)了這兩個(gè)接口读宙,而它的內(nèi)部,正是使用BeanDefinition來(lái)管理Bean:

DefaultListableBeanFactory.png

無(wú)論是通過(guò)代碼還是配置文件或者注解楔绞,本質(zhì)上都是通過(guò)BeanDefinitionReader先構(gòu)造BeanDefinition结闸,然后將BeanDefinition注冊(cè)到BeanDefinitionRegistry粱哼,通常也就是BeanFactory的實(shí)現(xiàn)透硝。

IoC容器的兩個(gè)階段

容器啟動(dòng)階段
  • 加載配置
  • 分析配置信息
  • 裝配到BeanDefinition
  • PostProcess

BeanFactoryPostProcessor是容器的擴(kuò)展機(jī)制,可在實(shí)例化階段之前對(duì)BeanDefinition中的信息進(jìn)行修改
如PropertyPlaceHolderConfigurer抠忘,把它聲明為Bean蔫耽,ApplicationContext會(huì)注冊(cè)并應(yīng)用它结耀,將bean配置中的占位符用屬性文件中的值替換

Bean實(shí)例化階段
  • 實(shí)例化對(duì)象
  • 裝配依賴(lài)
  • 生命周期回調(diào)
  • 對(duì)象的其他處理
  • 注冊(cè)回調(diào)接口
Bean生命周期
  1. 實(shí)例化
    BeanFactory根據(jù)BeanDefinition來(lái)進(jìn)行實(shí)例化,使用策略模式有反射和cglib兩種方式來(lái)實(shí)例化bean匙铡,返回一個(gè)包裹了bean的BeanWrapper图甜,用于第二步設(shè)置屬性
  2. 設(shè)置屬性
    BeanWrapperImpl同時(shí)也是繼承了PropertyEditorRegistry,使用PropertyEditor來(lái)轉(zhuǎn)換類(lèi)型慰枕,設(shè)置屬性
  3. Aware接口
    BeanFactory: BeanNameAware BeanClassLoaderAware BeanFactoryAware
    ApplicationContext: ResourceLoaderAware ApplicationEventPublisherAware MessageSourceAware ApplicationContextAware(都是ApplicationContext)
  4. BeanPostProcessor
public interface BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one; if
     * {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other BeanPostProcessor callbacks.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one; if
     * {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

常用場(chǎng)景:1.處理Aware接口 2.生成當(dāng)前對(duì)象的代理(AOP)
ApplicationContextAwareProcessor處理ApplicationContext相關(guān)的各個(gè)Aware接口具则。

用BeanPostProcessor處理Marker接口時(shí)(如Aware接口),其實(shí)這個(gè)接口相當(dāng)于為這個(gè)Bean添加了新的屬性具帮,接口一般包括的函數(shù)只有設(shè)置屬性和獲取屬性博肋,獲取屬性由Bean的Client調(diào)用,設(shè)置屬性的函數(shù)則由BeanPostProcessor調(diào)用來(lái)進(jìn)行設(shè)置蜂厅,所以這種Marker接口一般是在BeanPostProcessor的Before函數(shù)中處理匪凡,緊鄰著B(niǎo)ean實(shí)例化過(guò)程中的設(shè)置屬性(因?yàn)檫@也是設(shè)置屬性的一種,只不過(guò)是所有Bean統(tǒng)一設(shè)置)掘猿。

相對(duì)的病游,使用BeanPostProcessor來(lái)生成proxy時(shí)會(huì)在after函數(shù)中處理,這時(shí)候bean的初始化已經(jīng)完成稠通,可以被代理了衬衬。

  1. InitializingBean和init-method
    Bean的生命周期接口和生命周期方法

  2. 注冊(cè)銷(xiāo)毀回調(diào)
    以上的設(shè)置都完成之后,如果Bean實(shí)現(xiàn)了DisposableBean接口或者聲明了destroy-method屬性改橘,那么這時(shí)候容器會(huì)為這個(gè)Bean注冊(cè)一個(gè)用于銷(xiāo)毀的回調(diào)滋尉,當(dāng)關(guān)閉容器的時(shí)候,主動(dòng)調(diào)用一個(gè)函數(shù)飞主,來(lái)執(zhí)行bean的銷(xiāo)毀工作狮惜。

ConfigurableBeanFactory
/**
     * Destroy all singleton beans in this factory, including inner beans that have
     * been registered as disposable. To be called on shutdown of a factory.
     * <p>Any exception that arises during destruction should be caught
     * and logged instead of propagated to the caller of this method.
     */
    void destroySingletons();
ConfigurableApplicationContext
    /**
     * Register a shutdown hook with the JVM runtime, closing this context
     * on JVM shutdown unless it has already been closed at that time.
     * <p>This method can be called multiple times. Only one shutdown hook
     * (at max) will be registered for each context instance.
     * @see java.lang.Runtime#addShutdownHook
     * @see #close()
     */
    void registerShutdownHook();
  1. 使用

ApplicationContext

  • BeanFactoryPostProcessor高诺,BeabPostProcessor等特殊bean的自動(dòng)識(shí)別
  • bean實(shí)例的自動(dòng)初始化
  • 統(tǒng)一的資源加載策略
  • 國(guó)際化的信息支持
  • 容器內(nèi)事件發(fā)布
資源加載

問(wèn)題:

  1. java.net.URL局限于http,ftp,file等協(xié)議,而資源可以以二進(jìn)制碾篡、字節(jié)流虱而、文件等形式存在于文件系統(tǒng)、classpath开泽、URL定位等地方
  2. java.net.URL對(duì)資源的查找和定位沒(méi)有清晰的界限牡拇,返回的形式多種多樣,沒(méi)有統(tǒng)一的抽象

解決:
Resource與ResourceLoader

ClassPathResource class|classLoader#getResourceAsStream
FileSystemResource
UrlResource   UrlConnection#getInputStream
ByteArrayResource
InputStreamResource 

public interface Resource extends InputStreamSource {

    /**
     * Return whether this resource actually exists in physical form.
     * <p>This method performs a definitive existence check, whereas the
     * existence of a {@code Resource} handle only guarantees a
     * valid descriptor handle.
     */
    boolean exists();

    /**
     * Return whether the contents of this resource can be read,
     * e.g. via {@link #getInputStream()} or {@link #getFile()}.
     * <p>Will be {@code true} for typical resource descriptors;
     * note that actual content reading may still fail when attempted.
     * However, a value of {@code false} is a definitive indication
     * that the resource content cannot be read.
     * @see #getInputStream()
     */
    boolean isReadable();

    /**
     * Return whether this resource represents a handle with an open
     * stream. If true, the InputStream cannot be read multiple times,
     * and must be read and closed to avoid resource leaks.
     * <p>Will be {@code false} for typical resource descriptors.
     */
    boolean isOpen();

    /**
     * Return a URL handle for this resource.
     * @throws IOException if the resource cannot be resolved as URL,
     * i.e. if the resource is not available as descriptor
     */
    URL getURL() throws IOException;

    /**
     * Return a URI handle for this resource.
     * @throws IOException if the resource cannot be resolved as URI,
     * i.e. if the resource is not available as descriptor
     */
    URI getURI() throws IOException;

    /**
     * Return a File handle for this resource.
     * @throws IOException if the resource cannot be resolved as absolute
     * file path, i.e. if the resource is not available in a file system
     */
    File getFile() throws IOException;

    /**
     * Determine the content length for this resource.
     * @throws IOException if the resource cannot be resolved
     * (in the file system or as some other known physical resource type)
     */
    long contentLength() throws IOException;

    /**
     * Determine the last-modified timestamp for this resource.
     * @throws IOException if the resource cannot be resolved
     * (in the file system or as some other known physical resource type)
     */
    long lastModified() throws IOException;

    /**
     * Create a resource relative to this resource.
     * @param relativePath the relative path (relative to this resource)
     * @return the resource handle for the relative resource
     * @throws IOException if the relative resource cannot be determined
     */
    Resource createRelative(String relativePath) throws IOException;

    /**
     * Determine a filename for this resource, i.e. typically the last
     * part of the path: for example, "myfile.txt".
     * <p>Returns {@code null} if this type of resource does not
     * have a filename.
     */
    String getFilename();

    /**
     * Return a description for this resource,
     * to be used for error output when working with the resource.
     * <p>Implementations are also encouraged to return this value
     * from their {@code toString} method.
     * @see Object#toString()
     */
    String getDescription();

}
public interface InputStreamSource {

    /**
     * Return an {@link InputStream}.
     * <p>It is expected that each call creates a <i>fresh</i> stream.
     * <p>This requirement is particularly important when you consider an API such
     * as JavaMail, which needs to be able to read the stream multiple times when
     * creating mail attachments. For such a use case, it is <i>required</i>
     * that each {@code getInputStream()} call returns a fresh stream.
     * @return the input stream for the underlying resource (must not be {@code null})
     * @throws IOException if the stream could not be opened
     * @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
     */
    InputStream getInputStream() throws IOException;
}
public interface ResourceLoader {

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


    /**
     * Return a Resource handle for the specified resource.
     * The handle should always be a reusable resource descriptor,
     * allowing for multiple {@link Resource#getInputStream()} calls.
     * <p><ul>
     * <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
     * <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
     * <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
     * (This will be implementation-specific, typically provided by an
     * ApplicationContext implementation.)
     * </ul>
     * <p>Note that a Resource handle does not imply an existing resource;
     * you need to invoke {@link Resource#exists} to check for existence.
     * @param location the resource location
     * @return a corresponding Resource handle
     * @see #CLASSPATH_URL_PREFIX
     * @see org.springframework.core.io.Resource#exists
     * @see org.springframework.core.io.Resource#getInputStream
     */
    Resource getResource(String location);

    /**
     * Expose the ClassLoader used by this ResourceLoader.
     * <p>Clients which need to access the ClassLoader directly can do so
     * in a uniform manner with the ResourceLoader, rather than relying
     * on the thread context ClassLoader.
     * @return the ClassLoader (only {@code null} if even the system
     * ClassLoader isn't accessible)
     * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
     */
    ClassLoader getClassLoader();

}
DefaultResourceLoader#getResource

(1) 檢查路徑是否以classpath:開(kāi)頭->ClassPathResource
(2) a.通過(guò)Url來(lái)定位資源眼姐,構(gòu)造UrlResource诅迷;b.構(gòu)造ClassPathResource

FileSystemResourceLoader

修改了(2)b構(gòu)造FileSystemResource

ResourcePatternResolver

ResourceLoader的擴(kuò)展,添加了getResources
它的實(shí)現(xiàn)會(huì)委派一個(gè)ResouceLoader來(lái)加載資源

ApplicationContext

繼承了ResourcePatternResolver众旗,它的實(shí)現(xiàn)一般會(huì)繼承一個(gè)AbstractApplicationContext,這個(gè)類(lèi)會(huì)繼承DefaultReosurceLoader趟畏,而且有一個(gè)ResourcePatternResolver的對(duì)象贡歧,通過(guò)自己構(gòu)造。詳見(jiàn)Spring揭秘p91赋秀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末利朵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猎莲,更是在濱河造成了極大的恐慌绍弟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件著洼,死亡現(xiàn)場(chǎng)離奇詭異樟遣,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)身笤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)豹悬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人液荸,你說(shuō)我怎么就攤上這事瞻佛。” “怎么了娇钱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵伤柄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我文搂,道長(zhǎng)适刀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任细疚,我火速辦了婚禮蔗彤,結(jié)果婚禮上川梅,老公的妹妹穿的比我還像新娘。我一直安慰自己然遏,他們只是感情好贫途,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著待侵,像睡著了一般丢早。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秧倾,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天怨酝,我揣著相機(jī)與錄音,去河邊找鬼那先。 笑死农猬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的售淡。 我是一名探鬼主播斤葱,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼揖闸!你這毒婦竟也來(lái)了揍堕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤汤纸,失蹤者是張志新(化名)和其女友劉穎衩茸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贮泞,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楞慈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隙畜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抖部。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖议惰,靈堂內(nèi)的尸體忽然破棺而出慎颗,到底是詐尸還是另有隱情,我是刑警寧澤言询,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布俯萎,位于F島的核電站,受9級(jí)特大地震影響运杭,放射性物質(zhì)發(fā)生泄漏夫啊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一辆憔、第九天 我趴在偏房一處隱蔽的房頂上張望撇眯。 院中可真熱鬧报嵌,春花似錦、人聲如沸熊榛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)玄坦。三九已至血筑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間煎楣,已是汗流浹背豺总。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留择懂,地道東北人喻喳。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像休蟹,于是被迫代替她去往敵國(guó)和親沸枯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡(jiǎn)書(shū) 3.8 Container Exten...
    SnailTyan閱讀 1,228評(píng)論 0 6
  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息赂弓,并在Spring容器中生成一份相...
    Theriseof閱讀 2,796評(píng)論 1 24
  • 注意LifecycleProcessor接口繼承了Lifcycle接口。同時(shí)哪轿,增加了2個(gè)方法盈魁,用于處理容器的ref...
    google666s閱讀 1,099評(píng)論 0 51
  • 出現(xiàn)這種錯(cuò)誤的原因是 邊遍歷數(shù)組 邊修改數(shù)組中的內(nèi)容 解決辦法 1. 親測(cè)有效 NSMutableArray* a...
    騎驢去旅行閱讀 6,102評(píng)論 0 1
  • 時(shí)間:2016年5月17日 在昨天學(xué)習(xí)了邊框和尺寸的情況下,今天我們主要學(xué)習(xí)的就是盒模型窃诉。盒模型是HTML中最重要...
    旭先生閱讀 602評(píng)論 0 2