Spring動態(tài)生成Bean的定義-BeanDefinition源碼解析

BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor
ImportBeanDefinitionRegistrar

有的時候神汹,我們需要在spring運行的時候,根據(jù)需要 動態(tài)添加之前沒有定義的spring的bean,比如mybatis掃描某個包下的接口,轉(zhuǎn)化為Mapper Bean础废。

Bean的定義在Spring中的組件名叫:BeanDefinition饥伊。
當(dāng)我們創(chuàng)建了一個BeanDefinion后可以通過BeanDefinitionRegistry接口(ApplicationContext會實現(xiàn)此接口)將新的BeanDefinition注冊到spring上下文中。
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

BeanFactoryPostProcessor(Bean工廠的后置處理器)

這個接口的作用是在Spring上下文的注冊Bean定義的邏輯都跑完后智亮,但是所有的Bean都還沒真正實例化之前調(diào)用俄删。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
這個方法的主要用途就是通過注入進來的BeanFactory宏怔,在真正初始化Bean之前奏路,再對spring上下文做一些動態(tài)修改。增加或者修改某些Bean定義的值臊诊,甚至再動態(tài)創(chuàng)建一些BeanDefinition都可以鸽粉。
Demo:

@Override
  public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
    GenericBeanDefinition beanDefinition = getGenericBeanDefinition();

    ((DefaultListableBeanFactory) beanFactory)
        .registerBeanDefinition("dynamicUser", beanDefinition);
  }
private GenericBeanDefinition getGenericBeanDefinition() {
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(User.class);
    beanDefinition.getPropertyValues().add("name", "張三");
    return beanDefinition;
  }

BeanDefinitionRegistryPostProcessor(Bean注冊器的后置處理器)

如上我們可以定義一個有關(guān)User這個Bean的定義,并且預(yù)先設(shè)置此Bean的Name屬性值是
“張三”妨猩,最后注冊到beanFactory中潜叛。

有鑒于此秽褒,Spring特意提供了一個更具體的子接口BeanDefinitionRegistryPostProcessor
這個子接口的方法postProcessBeanDefinitionRegistry會被Spring先于postProcessBeanFactory這個方法執(zhí)行壶硅。不過呢,其實這2個方法都是注入的spring的上下文销斟,只不過聲明類型不同罷了庐椒。所以可以做的事一模一樣。

 private GenericBeanDefinition getGenericBeanDefinition() {
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(User.class);
    beanDefinition.getPropertyValues().add("name", "張三");
    return beanDefinition;
  }

  @Override
  public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
    GenericBeanDefinition beanDefinition = getGenericBeanDefinition();
    registry.registerBeanDefinition("dynamicUser2",beanDefinition);
  }

最后調(diào)用此spring上下文得到這個2個Bean:

public static void main(String[] args)
    {
        ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
        User dynamicUser = ctx.getBean("dynamicUser", User.class);
        System.out.println(dynamicUser);
        User dynamicUser2 = ctx.getBean("dynamicUser2", User.class);
        System.out.println(dynamicUser2);
    }

BeanDefinitionBuilder

建造者模式去創(chuàng)建一個BeanDefinition蚂踊。從2.5之后GenericBeanDefinition代替了原本的RootBeanDefinition和ChildBeanDefinition约谈。我們通常使用GenericBeanDefinition就足夠了。
我們通過Builder模式去設(shè)置BeanDefinition一些常用方法:

//設(shè)置Bean的構(gòu)造函數(shù)傳入的參數(shù)值
public BeanDefinitionBuilder addConstructorArgValue(Object value)
//設(shè)置構(gòu)造函數(shù)引用其他的bean
public BeanDefinitionBuilder addConstructorArgReference(String beanName)
//設(shè)置這個bean的 init方法和destory方法
public BeanDefinitionBuilder setInitMethodName(String methodName) 
public BeanDefinitionBuilder setDestroyMethodName(String methodName) 
//設(shè)置單例/多例
public BeanDefinitionBuilder setScope(String scope) 
//設(shè)置是否是個抽象的BeanDefinition犁钟,如果為true棱诱,表明這個BeanDefinition只是用來給子BeanDefinition去繼承的,Spring不會去嘗試初始化這個Bean涝动。
public BeanDefinitionBuilder setAbstract(boolean flag) 
//是否懶加載迈勋,默認(rèn)是false
public BeanDefinitionBuilder setLazyInit(boolean lazy) 
//自動注入依賴的模式,默認(rèn)不注入
public BeanDefinitionBuilder setAutowireMode(int autowireMode) 
//檢測依賴醋粟。
public BeanDefinitionBuilder setDependencyCheck(int dependencyCheck) 

以上靡菇,參考資料:http://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition/

ImportBeanDefinitionRegistrar

這個接口的實現(xiàn)類的作用是當(dāng)這個接口的實現(xiàn)類被@Import接口引入某個被標(biāo)記了@Configuration的注冊類時,可以得到這個類的所有注解米愿,然后做一些動態(tài)注冊Bean的事兒厦凤。
比如在使用spring cloud feign組件的時候,我們這些寫的

@SpringBootApplication
@EnableFeignClients(basePackages = "newbie.yyf.service.api.api")
public class Application {

觀察EnableFeignClients這個注解里面是不是就使用了
"@Import(FeignClientsRegistrar.class)"
而FeignClientsRegistrar就實現(xiàn)了ImportBeanDefinitionRegistrar接口育苟,所以這個接口的實現(xiàn):

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        registerDefaultConfiguration(metadata, registry);
        registerFeignClients(metadata, registry);
    }

這里面的AnnotationMetadata到底會包含什么呢较鼓?其實就是被Import標(biāo)記的Application這個注冊文件的所有注解。
我們可以通過拿到注解上的一些配置信息去動態(tài)生成BeanDefinition违柏,如果你熟悉Feign的作用的話笨腥,你可以想到主要是Feign的客戶端接口的Mock Bean,就像mybatis一樣勇垛。


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脖母,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闲孤,更是在濱河造成了極大的恐慌谆级,老刑警劉巖烤礁,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肥照,居然都是意外死亡脚仔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門舆绎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鲤脏,“玉大人,你說我怎么就攤上這事吕朵×源迹” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵努溃,是天一觀的道長硫嘶。 經(jīng)常有香客問我,道長梧税,這世上最難降的妖魔是什么沦疾? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮第队,結(jié)果婚禮上哮塞,老公的妹妹穿的比我還像新娘。我一直安慰自己凳谦,他們只是感情好忆畅,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晾蜘,像睡著了一般邻眷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剔交,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天肆饶,我揣著相機與錄音,去河邊找鬼岖常。 笑死驯镊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竭鞍。 我是一名探鬼主播板惑,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼偎快!你這毒婦竟也來了冯乘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤晒夹,失蹤者是張志新(化名)和其女友劉穎裆馒,沒想到半個月后姊氓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡喷好,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年翔横,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梗搅。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡禾唁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出无切,到底是詐尸還是另有隱情荡短,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布订雾,位于F島的核電站肢预,受9級特大地震影響矛洞,放射性物質(zhì)發(fā)生泄漏洼哎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一沼本、第九天 我趴在偏房一處隱蔽的房頂上張望噩峦。 院中可真熱鬧,春花似錦抽兆、人聲如沸识补。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凭涂。三九已至,卻和暖如春贴妻,著一層夾襖步出監(jiān)牢的瞬間切油,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工名惩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澎胡,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓娩鹉,卻偏偏與公主長得像攻谁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弯予,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理戚宦,服務(wù)發(fā)現(xiàn),斷路器锈嫩,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • Spring容器高層視圖 Spring 啟動時讀取應(yīng)用程序提供的Bean配置信息受楼,并在Spring容器中生成一份相...
    Theriseof閱讀 2,796評論 1 24
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評論 6 342
  • 雨落花嬌 青墻黛瓦 小巷落寞的身影中 誰家的白衣少年陌上無雙 粉面含嬌俏佳人 春雨潤物 璀璨欲滴 你是人間的四月天...
    高嘉縷閱讀 313評論 0 1
  • 早上跑步困檩,聽專欄,聽到一個詞語:刻意避開那槽。發(fā)現(xiàn)這個詞很有意思悼沿。 刻意避開的意思,就是盡量避開在自己不擅長的領(lǐng)域做決...
    逄格亮閱讀 461評論 0 1