訂閱
糾錯(cuò)
加入自媒體

Bean的定義與控制、純Java運(yùn)行與@Bean

2019-04-11 10:39
EAWorld
關(guān)注

全局配置初始化與銷(xiāo)毀方法

IoC容器還提供了全局配置初始化與銷(xiāo)毀方法的配置:

package x.y;public class A {    public void init(){        // 初始化資源    }    public void destroy(){        // 銷(xiāo)毀資源    }}

<beans default-init-method="init" default-destroy-method="destroy">     <bean id="a" class="x.y.A"/>     <!-- bean configuration --></beans>

通過(guò)在<beans>標(biāo)簽上使用default-init-method和default-destroy-method 屬性參數(shù),可以為容器中所有的Bean統(tǒng)一指定初始化和銷(xiāo)毀的生命周期方法。

如果在<beans>上設(shè)定2個(gè)默認(rèn)的生命周期方法,同時(shí)在<bean>上也指定了init-method或destroy-method,回調(diào)方法會(huì)以<bean>上的配置為準(zhǔn)。這樣就保證全局配置與單獨(dú)配置可以共存。

使用初始化或銷(xiāo)毀2個(gè)生命周期方法注意的要點(diǎn):

初始化和銷(xiāo)毀都提供了3種手段:XML配置、注解、以及實(shí)現(xiàn)接口。系統(tǒng)的各個(gè)部分會(huì)交由不同的團(tuán)隊(duì)開(kāi)發(fā),不遵循統(tǒng)一的規(guī)范,建議使用滿足JSR規(guī)范的注解——@PostConstruct、@PreDestroy。如果是統(tǒng)一的團(tuán)隊(duì),準(zhǔn)訓(xùn)一致的規(guī)范,建議使用<beans>的屬性統(tǒng)一名稱使用全局配置。

如果Bean設(shè)計(jì)到代理模式時(shí)(例如使用了AOP),那么生命周期方法被調(diào)用時(shí),有可能代理類還沒(méi)有被創(chuàng)建出來(lái)。因?yàn)樯芷诜椒ㄊ菍?shí)體類完成對(duì)應(yīng)工作之后就會(huì)被調(diào)用,而與代理類無(wú)關(guān)。

3.0新增容器啟動(dòng)方法

在3.0之前的Spring核心框架中,我們啟動(dòng)一個(gè)Spring容器必須使用一個(gè)XML文件。而到了3.X之后的版本Spring為創(chuàng)建容器新增了一個(gè)入口類——AnnotationConfigApplicationContext

AnnotationConfigApplicationContext和過(guò)去的ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等方法不同的是他不用再指定任何XML配置文件,而是可以通過(guò)指定類向容器添加Bean。我們通過(guò)幾個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明他的使用。

以下例子只用于說(shuō)明問(wèn)題,源碼請(qǐng)到 gitee 自行 clone(http://t.cn/E6Wvo51),本節(jié)的代碼在 chkui.springcore.example.javabase.simple 包中。

直接添加Bean

我們可以通過(guò)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));  }}

這樣就啟動(dòng)了一個(gè)Spring的容器,并且容器中包含了WowBean和LolBean這兩個(gè)類的單例。

替代<beans>標(biāo)簽

@Configuration在之前介紹Spring核心容器的文章中出現(xiàn)過(guò)一兩次,配合各種注解的使用@Configuration可以替代<beans>配置中的所有功能;旧螦nnotationConfigApplicationContext和@Configuration組合使用就可以實(shí)現(xiàn)Spring容器純Java啟動(dòng)。請(qǐng)看下面的例子。

我們?cè)谇懊胬拥幕A(chǔ)上增加幾個(gè)類:

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上是沒(méi)有@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();  }}

最后運(yùn)行他們:

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)》這篇文章介紹過(guò),@ComponentScan的作用等價(jià)于<context:component-scan/>標(biāo)簽,屬性參數(shù)都是一一對(duì)應(yīng)的,只不過(guò)前者是駝峰命名規(guī)則(camelCase)——@ComponentScan(basePackages="..."),后者是短橫線命名規(guī)則(kebab-case)——<context:component-scan base-package="..."/>。實(shí)際上使用Annotation來(lái)替換XML配置中的內(nèi)容,大部分都使用這種轉(zhuǎn)換方式。

@Configuration和@Bean標(biāo)簽會(huì)在后續(xù)的內(nèi)容中詳細(xì)介紹。@Bean主要用于方法標(biāo)記,表明這個(gè)方法返回一個(gè)要添加到容器中的Bean。

AnnotationConfigApplicationContext的其他使用方法

除了以上常規(guī)的使用方法,AnnotationConfigApplicationContext還有其他方式向容器添加Bean。

可以使用AnnotationConfigApplicationContext::register方法來(lái)添加配置和Bean:

public static void main(String[] args) {    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();    //動(dòng)態(tài)添加配置文件    ctx.register(Config1.class, Config2.class);    //動(dòng)態(tài)添加Bean    ctx.register(Bean1.class);    //刷新    ctx.refresh();}

注意最后的refresh方法,這個(gè)方法來(lái)源于ConfigurableApplicationContext接口,然后是在AbstractApplicationContext中實(shí)現(xiàn)的。他的過(guò)程相當(dāng)于銷(xiāo)毀之前已經(jīng)創(chuàng)建的資源,然后再重新創(chuàng)建了一個(gè)新的容器。這里的代碼會(huì)執(zhí)行以下幾步:

new AnnotationConfigApplicationContext():創(chuàng)建一個(gè)新的容器,容器中沒(méi)有自定義的Bean。

AnnotationConfigApplicationContext::register:向容器添加BeanDefinition(http://t.cn/E6WzQ7W),但是這些BeanDefinition并沒(méi)有轉(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()方法會(huì)拋出一個(gè)重復(fù)執(zhí)行refresh的異常。AnnotationConfigApplicationContext::refresh()方法全局也只能被調(diào)用一次。

@Bean注解

@Bean注解等價(jià)于配置文件中的<bean>標(biāo)簽,對(duì)應(yīng)的參數(shù)也是將短橫線命名切換為駝峰命名——<bean init-method="..."> => @Bean(initMethod="...")。@Bean注解只能使用在方法上,方法必須是在@Configuration標(biāo)記的類或者其他Bean中,兩者存在的差異會(huì)在后續(xù)的文章中介紹。下面通過(guò)一個(gè)例子來(lái)說(shuō)明Bean的使用。

以下例子只用于說(shuō)明問(wèn)題,源碼請(qǐng)到 gitee 自行 clone(http://t.cn/E6Wvo51),本節(jié)的代碼在 chkui.springcore.example.javabase.beanAnnotation 包中。

定義兩個(gè)要添加到容器中的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!");  }}

定義一個(gè)功能接口及其實(shí)現(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;  }}

然后頂一個(gè)@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();  }}

最后運(yùn)行他們:

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個(gè)Bean。這里的寫(xiě)在方法上的@Bean注解和寫(xiě)在配置文件中的<bean>注解一個(gè)效果:

@Bean中的initMethod和destroyMethod對(duì)應(yīng)<bean>標(biāo)簽中的init-method和destroy-method屬性。

@Bean中的name參數(shù)只有一個(gè)值時(shí)相當(dāng)于id,有多個(gè)的時(shí)候相當(dāng)于設(shè)置了多個(gè)別名

Support support(FinalFantasy ff):我們可以直接在方法中暴露參數(shù)來(lái)引入其他Bean,這就類似于配置中ref的功能。

如果不指定initMethod和destroyMethod,使用JSR-330的生命周期注解(@PostConstruct、@PreDestroy)同樣有效

關(guān)于作者:陳葵,目前現(xiàn)任職某跨境安全支付公司技術(shù)總監(jiān),中山大學(xué)密碼學(xué)與信息安全專業(yè)碩士。對(duì)金融級(jí)安全支付,高可用性云應(yīng)用,分布式事物、DevOps有多年的經(jīng)驗(yàn)。雖肩負(fù)團(tuán)隊(duì)管理的任務(wù),但對(duì)Coding依然保持極大的興趣,熟讀Spring、React、Tensorflow等各類開(kāi)源項(xiàng)目的核心代碼。目前主導(dǎo)通過(guò)數(shù)據(jù)分析+AI提升風(fēng)控模型能力的研究。

關(guān)于EAWorld:微服務(wù),DevOps,數(shù)據(jù)治理,移動(dòng)架構(gòu)原創(chuàng)技術(shù)分享。關(guān)注EAWorld!

<上一頁(yè)  1  2  
聲明: 本文由入駐維科號(hào)的作者撰寫(xiě),觀點(diǎn)僅代表作者本人,不代表OFweek立場(chǎng)。如有侵權(quán)或其他問(wèn)題,請(qǐng)聯(lián)系舉報(bào)。

發(fā)表評(píng)論

0條評(píng)論,0人參與

請(qǐng)輸入評(píng)論內(nèi)容...

請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字

您提交的評(píng)論過(guò)于頻繁,請(qǐng)輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無(wú)評(píng)論

暫無(wú)評(píng)論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號(hào)
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯(cuò)
x
*文字標(biāo)題:
*糾錯(cuò)內(nèi)容:
聯(lián)系郵箱:
*驗(yàn) 證 碼:

粵公網(wǎng)安備 44030502002758號(hào)