實(shí)力總結(jié)四類Bean注入Spring的方式

原創(chuàng):微信公眾號(hào) 【阿Q說(shuō)代碼】凝赛,歡迎分享,轉(zhuǎn)載請(qǐng)保留出處坛缕。

一提到Spring墓猎,大家最先想到的是啥?是AOPIOC的兩大特性赚楚?是SpringBean的初始化流程毙沾?還是基于SpringSpring Cloud全家桶呢?

今天我們就從SpringIOC特性入手宠页,聊一聊Spring中把Bean注入Spring容器的幾種方式左胞。

我們先來(lái)簡(jiǎn)單了解下IOC的概念:IOC控制反轉(zhuǎn),也稱為依賴注入举户,是指將對(duì)象的創(chuàng)建或者依賴關(guān)系的引用從具體的對(duì)象控制轉(zhuǎn)為框架或者IOC容器來(lái)完成烤宙,也就是依賴對(duì)象的獲得被反轉(zhuǎn)了。

可以簡(jiǎn)單理解為原來(lái)由我們來(lái)創(chuàng)建對(duì)象俭嘁,現(xiàn)在由Spring來(lái)創(chuàng)建并控制對(duì)象躺枕。

xml 方式

依稀記得最早接觸Spring的時(shí)候,用的還是SSH框架,不知道大家對(duì)這個(gè)還有印象嗎屯远?所有的bean的注入得依靠xml文件來(lái)完成蔓姚。

它的注入方式分為:set方法注入、構(gòu)造方法注入慨丐、字段注入坡脐,而注入類型分為值類型注入(8種基本數(shù)據(jù)類型)和引用類型注入(將依賴對(duì)象注入)。

以下是set方法注入的簡(jiǎn)單樣例

<bean name="teacher" class="org.springframework.demo.model.Teacher">
    <property name="name" value="阿Q"></property>
</bean>

對(duì)應(yīng)的實(shí)體類代碼

public class Teacher {

    private String name;

    public void setName(String name) {
        this.name = name;
    }
}

xml方式存在的缺點(diǎn)如下:

  1. xml文件配置起來(lái)比較麻煩房揭,既要維護(hù)代碼又要維護(hù)配置文件备闲,開(kāi)發(fā)效率低;
  2. 項(xiàng)目中配置文件過(guò)多捅暴,維護(hù)起來(lái)比較困難恬砂;
  3. 程序編譯期間無(wú)法對(duì)配置項(xiàng)的正確性進(jìn)行驗(yàn)證,只能在運(yùn)行期發(fā)現(xiàn)并且出錯(cuò)之后不易排查蓬痒;
  4. 解析xml時(shí)泻骤,無(wú)論是將xml一次性裝進(jìn)內(nèi)存,還是一行一行解析梧奢,都會(huì)占用內(nèi)存資源狱掂,影響性能。

注解方式

隨著Spring的發(fā)展亲轨,Spring 2.5開(kāi)始出現(xiàn)了一系列注解趋惨,除了我們經(jīng)常使用的@Controller、@Service惦蚊、@Repository器虾、@Component 之外,還有一些比較常用的方式蹦锋,接下來(lái)我們簡(jiǎn)單了解下兆沙。

@Configuration + @Bean

當(dāng)我們需要引入第三方的jar包時(shí),可以用@Bean注解來(lái)標(biāo)注莉掂,同時(shí)需要搭配@Configuration來(lái)使用葛圃。

  • @Configuration用來(lái)聲明一個(gè)配置類,可以理解為xml<beans>標(biāo)簽

  • @Bean 用來(lái)聲明一個(gè)bean巫湘,將其加入到Spring容器中,可以理解為xml<bean>標(biāo)簽

簡(jiǎn)單樣例:將 RedisTemplate 注入 Spring

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        ......
        return redisTemplate;
    }
}

@Import

我們?cè)诜?code>Spring源碼的過(guò)程中昏鹃,經(jīng)常會(huì)看到@Import注解尚氛,它也可以用來(lái)將第三方jar包注入Spring,但是它只可以作用在上洞渤。

例如在注解EnableSpringConfigured上就包含了@Import注解阅嘶,用于將SpringConfiguredConfiguration配置文件加載進(jìn)Spring容器。

@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}

@Importvalue值是一個(gè)數(shù)組,一個(gè)一個(gè)注入比較繁瑣讯柔,因此我們可以搭配ImportSelector接口來(lái)使用抡蛙,用法如下:

@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"org.springframework.demo.model.Teacher","org.springframework.demo.model.Student"};
    }
}

其中selectImports方法返回的數(shù)組就會(huì)通過(guò)@Import注解注入到Spring容器中。

無(wú)獨(dú)有偶魂迄,ImportBeanDefinitionRegistrar接口也為我們提供了注入bean的方法粗截。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    ......
}

我們點(diǎn)擊AspectJAutoProxyRegistrar類,發(fā)現(xiàn)它實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口捣炬,它的registerBeanDefinitions方法便是注入bean的過(guò)程熊昌,可以參考下。

如果覺(jué)得源代碼比較難懂湿酸,可以看一下我們自定義的類

@Configuration
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
            RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class);
            // 注冊(cè) Bean婿屹,并指定bean的名稱和類型
            registry.registerBeanDefinition("teacher", tDefinition);
        }
    }
}

這樣我們就把Teacher類注入到Spring容器中了。

FactoryBean

提到FactoryBean推溃,就不得不與BeanFactory比較一番昂利。

  • BeanFactory : 是 FactoryIOC容器或者對(duì)象工廠铁坎,所有的Bean都由它進(jìn)行管理
  • FactoryBean : 是Bean 蜂奸,是一個(gè)能產(chǎn)生或者修飾對(duì)象生成的工廠 Bean,實(shí)現(xiàn)與工廠模式和修飾器模式類似

那么FactoryBean是如何實(shí)現(xiàn)bean注入的呢厢呵?

先定義實(shí)現(xiàn)了FactoryBean接口的類

public class TeacherFactoryBean implements FactoryBean<Teacher> {

    /**
     * 返回此工廠管理的對(duì)象實(shí)例
     **/
    @Override
    public Teacher getObject() throws Exception {
        return new Teacher();
    }

    /**
     * 返回此 FactoryBean 創(chuàng)建的對(duì)象的類型
     **/
    @Override
    public Class<?> getObjectType() {
        return Teacher.class;
    }

}

然后通過(guò) @Configuration + @Bean的方式將TeacherFactoryBean加入到容器中

@Configuration
public class MyConfig {
    @Bean
    public TeacherFactoryBean teacherFactoryBean(){
        return new TeacherFactoryBean();
    }
}

注意:我們沒(méi)有向容器中注入Teacher, 而是直接注入的TeacherFactoryBean窝撵,然后從容器中拿Teacher這個(gè)類型的bean,成功運(yùn)行襟铭。

BDRegistryPostProcessor

看到這個(gè)接口碌奉,不知道對(duì)于翻看過(guò)Spring源碼的你來(lái)說(shuō)熟不熟悉。如果不熟悉的話請(qǐng)往下看寒砖,要是熟悉的話就再看一遍吧??赐劣。

源碼

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    // 注冊(cè)bean到spring容器中
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanFactoryPostProcessor接口是BeanFactory的后置處理器,方法postProcessBeanFactory對(duì)bean的定義進(jìn)行控制哩都。今天我們重點(diǎn)來(lái)看看postProcessBeanDefinitionRegistry方法:它的參數(shù)是BeanDefinitionRegistry魁兼,顧名思義就是與BeanDefinition注冊(cè)相關(guān)的。

1beanDefinition.jpg

通過(guò)觀察該類漠嵌,我們發(fā)現(xiàn)它里邊包含了registerBeanDefinition方法咐汞,這個(gè)不就是我們想要的嗎?為了能更好的使用該接口來(lái)達(dá)到注入bean的目的儒鹿,我們先來(lái)看看Spring是如何操作此接口的化撕。

2spring調(diào)用流程.jpg

看下invokeBeanFactoryPostProcessors方法,會(huì)發(fā)現(xiàn)沒(méi)有實(shí)現(xiàn)PriorityOrderedOrderedbean(這種跟我們自定義的實(shí)現(xiàn)類有關(guān))會(huì)執(zhí)行以下代碼约炎。

while (reiterate) {
    ......
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    ......
}

進(jìn)入該方法

private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, 
    BeanDefinitionRegistry registry) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

會(huì)發(fā)現(xiàn)實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口的bean植阴,其postProcessBeanDefinitionRegistry方法會(huì)被調(diào)用蟹瘾,也就是說(shuō)如果我們自定義接口實(shí)現(xiàn)該接口,它的postProcessBeanDefinitionRegistry方法也會(huì)被執(zhí)行掠手。

實(shí)戰(zhàn)

話不多說(shuō)憾朴,直接上代碼。自定義接口實(shí)現(xiàn)類

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 初始化過(guò)程中先執(zhí)行
     **/
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Teacher.class);
        //Teacher 的定義注冊(cè)到spring容器中
        registry.registerBeanDefinition("teacher", rootBeanDefinition);
    }

    /**
     * 初始化過(guò)程中后執(zhí)行
     **/
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

啟動(dòng)類代碼

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    MyBeanDefinitionRegistryPostProcessor postProcessor = new MyBeanDefinitionRegistryPostProcessor();
    //將自定義實(shí)現(xiàn)類加入 Spring 容器
    context.addBeanFactoryPostProcessor(postProcessor);
    context.refresh();
    Teacher bean = context.getBean(Teacher.class);
    System.out.println(bean);
}

啟動(dòng)并打印結(jié)果

org.springframework.demo.model.Teacher@2473d930

發(fā)現(xiàn)已經(jīng)注入到Spring容器中了喷鸽。

以上就是我們總結(jié)的幾種將bean注入Spring容器的方式众雷,趕快行動(dòng)起來(lái)實(shí)戰(zhàn)演練一下吧!

阿Q將持續(xù)更新java實(shí)戰(zhàn)方面的文章魁衙,感興趣的可以關(guān)注下报腔,也可以來(lái)技術(shù)群討論問(wèn)題呦!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剖淀,一起剝皮案震驚了整個(gè)濱河市纯蛾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纵隔,老刑警劉巖翻诉,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異捌刮,居然都是意外死亡碰煌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門绅作,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芦圾,“玉大人,你說(shuō)我怎么就攤上這事俄认「錾伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵眯杏,是天一觀的道長(zhǎng)夜焦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)岂贩,這世上最難降的妖魔是什么茫经? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮萎津,結(jié)果婚禮上卸伞,老公的妹妹穿的比我還像新娘。我一直安慰自己锉屈,他們只是感情好荤傲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著部念,像睡著了一般弃酌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上儡炼,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天妓湘,我揣著相機(jī)與錄音,去河邊找鬼乌询。 笑死榜贴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妹田。 我是一名探鬼主播唬党,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鬼佣!你這毒婦竟也來(lái)了驶拱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晶衷,失蹤者是張志新(化名)和其女友劉穎蓝纲,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晌纫,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡税迷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锹漱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箭养。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哥牍,靈堂內(nèi)的尸體忽然破棺而出毕泌,到底是詐尸還是另有隱情,我是刑警寧澤砂心,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布懈词,位于F島的核電站,受9級(jí)特大地震影響辩诞,放射性物質(zhì)發(fā)生泄漏坎弯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一译暂、第九天 我趴在偏房一處隱蔽的房頂上張望抠忘。 院中可真熱鬧,春花似錦外永、人聲如沸崎脉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)囚灼。三九已至骆膝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灶体,已是汗流浹背阅签。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝎抽,地道東北人政钟。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像樟结,于是被迫代替她去往敵國(guó)和親养交。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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