解讀Spring定義Bean的兩種方式:<bean>和@Bean
前言
Spring中最重要的概念I(lǐng)OC和AOP,實(shí)際圍繞的就是Bean的生成與使用。
什么叫做Bean呢?我們可以理解成對(duì)象,每一個(gè)你想交給Spring去托管的對(duì)象都可以稱(chēng)之為Bean。
今天通過(guò)Spring官方文檔來(lái)了解下,如何生成bean,如何使用呢?
1.通過(guò)XML的方式來(lái)生成一個(gè)bean
最簡(jiǎn)單也是最原始的一種方式,通過(guò)XML來(lái)定義一個(gè)bean,我們來(lái)看下其過(guò)程
1)創(chuàng)建entity,命名為Student
@Data @AllArgsConstructor @NoArgsConstructor public class Student implements Serializable { ? ? ? private static final long serialVersionUID = -2088281526481179972L; ? ? private int id; ? ? private String name; ? ? private int age; }? ??
2)在beans.xml中定義Student
? ? <!-- 1.空值的student --> ? ? <bean id="studentNoValue" class="domain.Student"/> ? ? ? <!-- 2.帶值的student --> ? ? <bean id="student" class="domain.Student"> ? ? ? ? <property name="id" value="11"/> ? ? ? ? <property name="age" value="22"/> ? ? ? ? <property name="name" value="jack"/> ? ? </bean> ? ? ? <!-- 3.全參構(gòu)造:使用成員變量名稱(chēng)對(duì)應(yīng) --> ? ? <bean id="studentConstruct" class="domain.Student"> ? ? ? ? <constructor-arg name="age" value="22"></constructor-arg> ? ? ? ? <constructor-arg name="id" value="11"></constructor-arg> ? ? ? ? <constructor-arg name="name" value="jack"></constructor-arg> ? ? </bean> ? ? ? <!-- 4.全參構(gòu)造:使用成員變量index對(duì)應(yīng) --> ? ? <bean id="studentConstruct2" class="domain.Student"> ? ? ? ? <constructor-arg index="0" value="11"></constructor-arg> ? ? ? ? <constructor-arg index="1" value="jack"></constructor-arg> ? ? ? ? <constructor-arg index="2" value="22"></constructor-arg> ? ? </bean> ? ? ? <!-- 5.全參構(gòu)造:使用成員變量類(lèi)型對(duì)應(yīng) --> ? ? <bean id="studentConstruct3" class="domain.Student"> ? ? ? ? <constructor-arg type="int" value="11"></constructor-arg> ? ? ? ? <constructor-arg type="java.lang.String" value="jack"></constructor-arg> ? ? ? ? <constructor-arg type="int" value="22"></constructor-arg> ? ? </bean>
總結(jié):可以看到,創(chuàng)建bean的方式多種多樣,我們可以通過(guò)屬性來(lái)賦值<property>,也可以通過(guò)構(gòu)造參數(shù)來(lái)賦值<constructor>,關(guān)于構(gòu)造賦值以上展示了三種方式,我們可以根據(jù)自己的需求來(lái)選擇對(duì)應(yīng)的方式。
3)測(cè)試bean
public class ApplicationContextTest { ? ? ? @Test ? ? public void testXml(){ ? ? ? ? ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); ? ? ? ? Student studentNoValue = (Student) applicationContext.getBean("studentNoValue"); ? ? ? ? Student studentFullValue = (Student) applicationContext.getBean("studentFullValue"); ? ? ? ? System.out.println(studentNoValue); ? ? ? ? System.out.println(studentFullValue); ? ? ? ? ? ? Student studentConstruct1 = (Student) applicationContext.getBean("studentConstruct"); ? ? ? ? Student studentConstruct2 = (Student) applicationContext.getBean("studentConstruct2"); ? ? ? ? Student studentConstruct3 = (Student) applicationContext.getBean("studentConstruct3"); ? ? ? ? System.out.println(studentConstruct1); ? ? ? ? System.out.println(studentConstruct2); ? ? ? ? System.out.println(studentConstruct3); ? ? ? ? ? Book bookChinese = (Book) applicationContext.getBean("bookChinese"); ? ? ? ? System.out.println(bookChinese); ? ? } } // res: Student(id=0, name=null, age=0) Student(id=11, name=jack, age=22) Student(id=11, name=jack, age=22) Student(id=11, name=jack, age=22) Student(id=11, name=jack, age=22)
2.<bean>標(biāo)簽深入了解
我們剛才介紹了最基本的Bean使用方式,大家會(huì)發(fā)現(xiàn)<bean>標(biāo)簽還有其他的屬性,比如name/scope/lazy-init/init-method/...等,這些是做什么用的呢?我們?cè)趯?shí)際的工作中怎么使用呢?
1)name屬性
在介紹name屬性之前,我們先來(lái)看下ApplicationContext.getBean()的兩種方式
* ApplicationContext.getBean(String name)
* ApplicationContext.getBean(Class<T> requiredType)
第一種方式的這個(gè)name是什么呢?我們應(yīng)該如何定義,又該如何使用呢?
// 上文示例中,我們只是指定了Bean的id和class,如下所示 <bean id="studentNoValue" class="domain.Student" /> ? ?? // 具體獲取bean的方式如下: Student studentNoValue = (Student) applicationContext.getBean("studentNoValue"); ? // 可以看到,在沒(méi)有指定bean的name屬性的時(shí)候,默認(rèn)使用id來(lái)獲取bean,當(dāng)做name使用 // 如果我們不想根據(jù)id獲取,那就需要主動(dòng)指定bean的name屬性,如下所示: <bean id="studentNoValue" class="domain.Student" name="stuName"/> // 這樣在獲取的時(shí)候,就需要使用指定的名稱(chēng)來(lái)獲取,再根據(jù)id來(lái)獲取的時(shí)候就會(huì)報(bào)錯(cuò)了 Student studentNoValue = (Student) applicationContext.getBean("stuName"); ? ? * 根據(jù)Class來(lái)獲取這種方式很好理解,這個(gè)不關(guān)心你定義的id或者name是什么,使用如下: Student studentNoValue = (Student) applicationContext.getBean(Student.class);
2)scope屬性
可以看到,在使用scope屬性的時(shí)候,提示有兩種輸入值,分別是singleton/prototype
這個(gè)就代表了Spring-Bean的兩種創(chuàng)建模式,單例模式和原型模式
* Spring默認(rèn)使用單例模式來(lái)創(chuàng)建Bean,通過(guò)ApplicationContext所獲得的bean都是同一個(gè)bean(在beanName相同的情況下),我們可以來(lái)驗(yàn)證下
Student studentNoValue = (Student) applicationContext.getBean("stuName"); Student studentNoValue2 = (Student) applicationContext.getBean("stuName"); ? System.out.println(studentNoValue == studentNoValue2);// true
可以看到的是結(jié)果輸入為true,從工廠類(lèi)中兩次獲取的stuName是同一個(gè)對(duì)象。
* 下面來(lái)驗(yàn)證下原型模式
原型模式:每次獲取的bean都為一個(gè)新的對(duì)象
// 修改beans.xml中studentNoValue的scope為prototype <bean id="studentNoValue" class="domain.Student" name="stuName" scope="prototype"/> ? // 然后執(zhí)行上面的測(cè)試代碼 Student studentNoValue = (Student) applicationContext.getBean("stuName"); Student studentNoValue2 = (Student) applicationContext.getBean("stuName"); ? System.out.println(studentNoValue == studentNoValue2);// false
可以看到,輸出結(jié)果為false,原型模式下從工廠類(lèi)兩次獲取的stuName不是同一個(gè)對(duì)象。
3)init-method和destroy-method方法
見(jiàn)名知意,init-method應(yīng)該是初始化方法的意思,destroy-method應(yīng)該是銷(xiāo)毀方法的意思。那怎么使用呢?
// 在Student.java中添加init()方法和destroy()方法 public void init(){ ? ? System.out.println("student init..."); } ? public void destroy(){ ? ? System.out.println("student destroy..."); } ? // 在beans.xml中studentNoValue的bean上添加 init-method和destroy-method <bean id="studentNoValue" class="domain.Student" name="stuName" init-method="init" destroy-method="destroy"/> ? // 測(cè)試方法如下: ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student studentNoValue = (Student) applicationContext.getBean("stuName"); ? applicationContext.close(); ? // 執(zhí)行結(jié)果: student init... student destroy... ? ?
總結(jié):在獲取bean的時(shí)候init()方法被執(zhí)行,在容器被銷(xiāo)毀的時(shí)候,執(zhí)行了destroy()方法
根據(jù)這個(gè),我們可以在初始化bean和銷(xiāo)毀bean的時(shí)候做點(diǎn)什么,比如關(guān)閉連接,保存記錄之類(lèi)的操作。
延伸:那么初始化init()方法在構(gòu)造方法之前調(diào)用,還是之后調(diào)用呢?讀者可以自行驗(yàn)證下
總結(jié):還有一些其他屬性,筆者就不再一一驗(yàn)證了,下面說(shuō)一下通過(guò)JavaConfig的方法來(lái)實(shí)現(xiàn)bean的定義。
3.JavaConfig方式的bean定義
JavaConfig是Spring4.x推薦的配置方式,可以完全替代XML的方式定義。
1)如何定義一個(gè)Bean
// 創(chuàng)建一個(gè)類(lèi),命名為SpringConfiguration @Configuration public class SpringConfiguration { ? ? @Bean ? ? public Student student(){ ? ? ? ? return new Student(11,"jack",22); ? ? } } ? // 使用bean AnnotationConfigApplicationContext applicationContext ? ? ? ? ? ? ? ? = new AnnotationConfigApplicationContext(SpringConfiguration.class); Student student = (Student) applicationContext.getBean("student") System.out.println(student); ? // res: Student(id=11, name=jack, age=22)
相對(duì)于XML的使用方式而言,JavaConfig的使用方式基本是同步的
* @Configuration等同于<beans></beans>
* @Bean等同于<bean></bean>
* 通過(guò)AnnotationConfigApplicationContext來(lái)加載JavaConfig
* 方法名student()就等同于<bean>中的id,默認(rèn)方法名就是beanName
2)@Bean的其他參數(shù)
* name屬性等同于<bean>的name
* initMethod屬性等同于<bean>的init-method
* destroyMethod屬性等同于<bean>的destroy-method
* scope這個(gè)比較奇怪,不屬于@Bean的參數(shù),這是一個(gè)單獨(dú)的注解,使用方式如下
@Bean(name = "stu",autowire = Autowire.BY_TYPE) @Scope(value = "singleton") public Student student(){ ? ? return new Student(11,"jack",22); }
總結(jié)
以上全文,我們通過(guò)兩種方式來(lái)定義一個(gè)Bean,默認(rèn)推薦JavaConfig。
還有一些其他屬性,筆者就不一一列舉了。
這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
參考:
相關(guān)文章
Springboot?2.x?RabbitTemplate默認(rèn)消息持久化的原因解析
這篇文章主要介紹了Springboot?2.x?RabbitTemplate默認(rèn)消息持久化的原因解析,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03JAVA實(shí)戰(zhàn)項(xiàng)目實(shí)現(xiàn)客戶選購(gòu)系統(tǒng)詳細(xì)流程
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶選購(gòu)系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-10-10java char數(shù)據(jù)類(lèi)型原理解析
這篇文章主要介紹了java char數(shù)據(jù)類(lèi)型原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02java實(shí)現(xiàn)的導(dǎo)出Excel工具類(lèi)實(shí)例
這篇文章主要介紹了java實(shí)現(xiàn)的導(dǎo)出Excel工具類(lèi),結(jié)合具體實(shí)例形式分析了java導(dǎo)出Excel導(dǎo)出并生成Excel表格相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-10-10IntelliJ Idea SpringBoot 數(shù)據(jù)庫(kù)增刪改查實(shí)例詳解
SpringBoot 是 SpringMVC 的升級(jí),對(duì)于編碼、配置、部署和監(jiān)控,更加簡(jiǎn)單。這篇文章主要介紹了IntelliJ Idea SpringBoot 數(shù)據(jù)庫(kù)增刪改查實(shí)例,需要的朋友可以參考下2018-02-02restTemplate發(fā)送get與post請(qǐng)求并且?guī)?shù)問(wèn)題
這篇文章主要介紹了restTemplate發(fā)送get與post請(qǐng)求并且?guī)?shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07SpringBoot+Vue項(xiàng)目部署實(shí)現(xiàn)傳統(tǒng)方式
我們?cè)谶M(jìn)行前后端分離開(kāi)發(fā)的時(shí)候,一般是將前端項(xiàng)目部署到nginx服務(wù)器上,與后端項(xiàng)目分開(kāi)部署,這篇文章主要給大家介紹了關(guān)于SpringBoot+Vue項(xiàng)目部署實(shí)現(xiàn)傳統(tǒng)方式的相關(guān)資料,需要的朋友可以參考下2024-01-01