Bean的定義與控制、純Java運行與@Bean
全局配置初始化與銷毀方法
IoC容器還提供了全局配置初始化與銷毀方法的配置:
package x.y;public class A { public void init(){ // 初始化資源 } public void destroy(){ // 銷毀資源 }}
<beans default-init-method="init" default-destroy-method="destroy"> <bean id="a" class="x.y.A"/> <。 bean configuration --></beans>
通過在<beans>標(biāo)簽上使用default-init-method和default-destroy-method 屬性參數(shù),可以為容器中所有的Bean統(tǒng)一指定初始化和銷毀的生命周期方法。
如果在<beans>上設(shè)定2個默認(rèn)的生命周期方法,同時在<bean>上也指定了init-method或destroy-method,回調(diào)方法會以<bean>上的配置為準(zhǔn)。這樣就保證全局配置與單獨配置可以共存。
使用初始化或銷毀2個生命周期方法注意的要點:
初始化和銷毀都提供了3種手段:XML配置、注解、以及實現(xiàn)接口。系統(tǒng)的各個部分會交由不同的團隊開發(fā),不遵循統(tǒng)一的規(guī)范,建議使用滿足JSR規(guī)范的注解——@PostConstruct、@PreDestroy。如果是統(tǒng)一的團隊,準(zhǔn)訓(xùn)一致的規(guī)范,建議使用<beans>的屬性統(tǒng)一名稱使用全局配置。
如果Bean設(shè)計到代理模式時(例如使用了AOP),那么生命周期方法被調(diào)用時,有可能代理類還沒有被創(chuàng)建出來。因為生命周期方法是實體類完成對應(yīng)工作之后就會被調(diào)用,而與代理類無關(guān)。
3.0新增容器啟動方法
在3.0之前的Spring核心框架中,我們啟動一個Spring容器必須使用一個XML文件。而到了3.X之后的版本Spring為創(chuàng)建容器新增了一個入口類——AnnotationConfigApplicationContext。
AnnotationConfigApplicationContext和過去的ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等方法不同的是他不用再指定任何XML配置文件,而是可以通過指定類向容器添加Bean。我們通過幾個簡單的例子來說明他的使用。
以下例子只用于說明問題,源碼請到 gitee 自行 clone(http://t.cn/E6Wvo51),本節(jié)的代碼在 chkui.springcore.example.javabase.simple 包中。
直接添加Bean
我們可以通過AnnotationConfigApplicationContext直接向容器添加指定的類作為Bean,先定義我們的class:
package chkui.springcore.example.javabase.simple.pureBean;
class LolBean { public String toString() { return "I AM LOL。ⅲ }}
class WowBean { public String toString() { return "I AM WOW。; }}
然后向容器添加這些Bean:
package chkui.springcore.example.javabase.simple;
public class WithoutAnnotation { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(WowBean.class, LolBean.class); System.out.println(ctx.getBean(WowBean.class)); System.out.println(ctx.getBean(LolBean.class)); }}
這樣就啟動了一個Spring的容器,并且容器中包含了WowBean和LolBean這兩個類的單例。
替代<beans>標(biāo)簽
@Configuration在之前介紹Spring核心容器的文章中出現(xiàn)過一兩次,配合各種注解的使用@Configuration可以替代<beans>配置中的所有功能;旧螦nnotationConfigApplicationContext和@Configuration組合使用就可以實現(xiàn)Spring容器純Java啟動。請看下面的例子。
我們在前面例子的基礎(chǔ)上增加幾個類:
package chkui.springcore.example.javabase.simple.bean;
public class DotaBean { public String toString() { return "I AM Dota。ⅲ }}
@Componentpublic class PseBean {
@Override public String toString() { return "I AM PSE。; }}
注意DotaBean上是沒有@Component注解的。然后添加@Configuration配置:
package chkui.springcore.example.javabase.simple.bean;
@Configuration@ComponentScan("chkui.springcore.example.javabase.simple.bean")public class Config { @Bean public DotaBean dotaBean() { return new DotaBean(); }}
最后運行他們:
package chkui.springcore.example.javabase.simple;
public class WithScan { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class, WowBean.class, LolBean.class); System.out.println(ctx.getBean(Config.class)); System.out.println(ctx.getBean(PseBean.class)); System.out.println(ctx.getBean(WowBean.class)); System.out.println(ctx.getBean(LolBean.class)); System.out.println(ctx.getBean(DotaBean.class)); }}
@Component已經(jīng)在《Stereotype組件與Bean掃描(http://t.cn/E6WhYYk)》這篇文章介紹過,@ComponentScan的作用等價于<context:component-scan/>標(biāo)簽,屬性參數(shù)都是一一對應(yīng)的,只不過前者是駝峰命名規(guī)則(camelCase)——@ComponentScan(basePackages="..."),后者是短橫線命名規(guī)則(kebab-case)——<context:component-scan base-package="..."/>。實際上使用Annotation來替換XML配置中的內(nèi)容,大部分都使用這種轉(zhuǎn)換方式。
@Configuration和@Bean標(biāo)簽會在后續(xù)的內(nèi)容中詳細介紹。@Bean主要用于方法標(biāo)記,表明這個方法返回一個要添加到容器中的Bean。
AnnotationConfigApplicationContext的其他使用方法
除了以上常規(guī)的使用方法,AnnotationConfigApplicationContext還有其他方式向容器添加Bean。
可以使用AnnotationConfigApplicationContext::register方法來添加配置和Bean:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //動態(tài)添加配置文件 ctx.register(Config1.class, Config2.class); //動態(tài)添加Bean ctx.register(Bean1.class); //刷新 ctx.refresh();}
注意最后的refresh方法,這個方法來源于ConfigurableApplicationContext接口,然后是在AbstractApplicationContext中實現(xiàn)的。他的過程相當(dāng)于銷毀之前已經(jīng)創(chuàng)建的資源,然后再重新創(chuàng)建了一個新的容器。這里的代碼會執(zhí)行以下幾步:
new AnnotationConfigApplicationContext():創(chuàng)建一個新的容器,容器中沒有自定義的Bean。
AnnotationConfigApplicationContext::register:向容器添加BeanDefinition(http://t.cn/E6WzQ7W),但是這些BeanDefinition并沒有轉(zhuǎn)化為容器中的Bean。
ConfigurableApplicationContext::refresh():納入新添加的BeanDefinition重建容器。
還可以直接使用AnnotationConfigApplicationContext::scan方法掃描指定的路徑:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.a(chǎn)cme"); ctx.refresh();}
執(zhí)行原理和上面介紹的一樣。
需要注意的是:如果你的工程中需要使用AnnotationConfigApplicationContext::register、AnnotationConfigApplicationContext::scan等方法創(chuàng)建容器和其中Bean的依賴關(guān)系,所有的Bean都只能在register或scan中添加。如果你既在AnnotationConfigApplicationContext的構(gòu)造方法中添加了Bean,又使用AnnotationConfigApplicationContext::refresh()方法會拋出一個重復(fù)執(zhí)行refresh的異常。AnnotationConfigApplicationContext::refresh()方法全局也只能被調(diào)用一次。
@Bean注解
@Bean注解等價于配置文件中的<bean>標(biāo)簽,對應(yīng)的參數(shù)也是將短橫線命名切換為駝峰命名——<bean init-method="..."> => @Bean(initMethod="...")。@Bean注解只能使用在方法上,方法必須是在@Configuration標(biāo)記的類或者其他Bean中,兩者存在的差異會在后續(xù)的文章中介紹。下面通過一個例子來說明Bean的使用。
以下例子只用于說明問題,源碼請到 gitee 自行 clone(http://t.cn/E6Wvo51),本節(jié)的代碼在 chkui.springcore.example.javabase.beanAnnotation 包中。
定義兩個要添加到容器中的Bean:
package chkui.springcore.example.javabase.beanAnnotation.bean;
class FinalFantasy { @Override public String toString() { return "Final Fantasy 1~15"; } public void init() { System.out.println("Final Fantasy init!"); } public void destroy() { System.out.println("Final Fantasy destroy。ⅲ; }}
class DragonQuest { public String toString() { return "Dragon Quest 1~11"; } @PostConstruct public void init() { System.out.println("Dragon Quest init。ⅲ } @PreDestroy public void destroy() { System.out.println("Dragon Quest destroy。ⅲ; }}
定義一個功能接口及其實現(xiàn)類:
package chkui.springcore.example.javabase.beanAnnotation.bean;
interface Support { void setFinalFantasy(FinalFantasy ff); FinalFantasy getFinalFantasy();}class SupportImpl implements Support { private FinalFantasy ff; public void setFinalFantasy(FinalFantasy ff) { this.ff = ff; } public FinalFantasy getFinalFantasy() { return ff; }}
然后頂一個@Configuration類:
package chkui.springcore.example.javabase.beanAnnotation.bean;
public class BeanAnnotationConfig { @Bean public Support support(FinalFantasy ff) { Support support = new SupportImpl(); support.setFinalFantasy(ff); return support; } @Bean(initMethod="init", destroyMethod="destroy") @Description("Final Fantasy") public FinalFantasy finalFantasy() { return new FinalFantasy(); } @Bean(name= {"dragon-quest", "DragonQuest"}) public DragonQuest dragonQuest() { return new DragonQuest(); }}
最后運行他們:
public class BeanAnnotApp {
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanAnnotationConfig.class); Support support = ctx.getBean(Support.class); System.out.println(support.getFinalFantasy()); System.out.println(ctx.getBean(DragonQuest.class)); }
}
在配置類BeanAnnotationConfig中,我們配置了3個Bean。這里的寫在方法上的@Bean注解和寫在配置文件中的<bean>注解一個效果:
@Bean中的initMethod和destroyMethod對應(yīng)<bean>標(biāo)簽中的init-method和destroy-method屬性。
@Bean中的name參數(shù)只有一個值時相當(dāng)于id,有多個的時候相當(dāng)于設(shè)置了多個別名
Support support(FinalFantasy ff):我們可以直接在方法中暴露參數(shù)來引入其他Bean,這就類似于配置中ref的功能。
如果不指定initMethod和destroyMethod,使用JSR-330的生命周期注解(@PostConstruct、@PreDestroy)同樣有效
關(guān)于作者:陳葵,目前現(xiàn)任職某跨境安全支付公司技術(shù)總監(jiān),中山大學(xué)密碼學(xué)與信息安全專業(yè)碩士。對金融級安全支付,高可用性云應(yīng)用,分布式事物、DevOps有多年的經(jīng)驗。雖肩負(fù)團隊管理的任務(wù),但對Coding依然保持極大的興趣,熟讀Spring、React、Tensorflow等各類開源項目的核心代碼。目前主導(dǎo)通過數(shù)據(jù)分析+AI提升風(fēng)控模型能力的研究。
關(guān)于EAWorld:微服務(wù),DevOps,數(shù)據(jù)治理,移動架構(gòu)原創(chuàng)技術(shù)分享。關(guān)注EAWorld!

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
推薦專題
- 1 UALink規(guī)范發(fā)布:挑戰(zhàn)英偉達AI統(tǒng)治的開始
- 2 北電數(shù)智主辦酒仙橋論壇,探索AI產(chǎn)業(yè)發(fā)展新路徑
- 3 降薪、加班、裁員三重暴擊,“AI四小龍”已折戟兩家
- 4 “AI寒武紀(jì)”爆發(fā)至今,五類新物種登上歷史舞臺
- 5 國產(chǎn)智駕迎戰(zhàn)特斯拉FSD,AI含量差幾何?
- 6 光計算迎來商業(yè)化突破,但落地仍需時間
- 7 東陽光:2024年扭虧、一季度凈利大增,液冷疊加具身智能打開成長空間
- 8 地平線自動駕駛方案解讀
- 9 封殺AI“照騙”,“淘寶們”終于不忍了?
- 10 優(yōu)必選:營收大增主靠小件,虧損繼續(xù)又逢關(guān)稅,能否乘機器人東風(fēng)翻身?