Spring Bean詳細講解
什麼是Bean?
Spring Bean是被例項的,組裝的及被Spring 容器管理的Java物件。
Spring 容器會自動完成@bean物件的例項化。
建立應用物件之間的協作關係的行為稱為:裝配(wiring),這就是依賴注入的本質。
Spring 三種配置方案
1.在XML中進行顯示配置
2.使用Java程式碼進行顯示配置
3.隱式的bean發現機制和自動裝配
推薦方式:3>2>1
一、自動化裝配bean
1.元件掃描(component scanning):Spring 會自動發現應用上下文中所建立的bean。
2.自動裝配(autowiring):Spring自動滿足bean之間的依賴。
- package com.stalkers;
- /**
- * CD唱片介面
- * Created by stalkers on 2016/11/17.
- */
- public interface ICompactDisc {
- void play();
- }
- package com.stalkers.impl;
- import com.stalkers.ICompactDisc;
-
import org.springframework.stereotype.Component;
- /**
- * Jay同名專輯
- * Created by stalkers on 2016/11/17.
- */
- public class JayDisc implements ICompactDisc {
- private String title = "星晴";
- public void play() {
-
System.out.println(title + ":一步兩步三步四步,望著天上星星...");
- }
- }
Component註解作用:
表明該類會作為元件類。
不過,元件掃描預設是不開啟用的,我們還需要顯示配置下Spring,從而命令它去尋找帶有@Component註解的類,併為其建立bean。
1.java code開啟元件掃描:
其中,如果CompoentScan後面沒有引數的話,預設會掃描與配置類相同的包
- public class CDPlayerConfig {
- public ICompactDisc disc() {
- return new JayDisc();
- }
- }
2.xml啟動元件掃描
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd">
- <context:component-scan base-package="com.stalkers.impl"/>
- </beans>
測試程式碼
- package com.stalkers;
- import com.stalkers.config.CDPlayerConfig;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- /**
- * Created by stalkers on 2016/11/18.
- */
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(classes = CDPlayerConfig.class)
- public class TestPlay {
- @Autowired
- private ICompactDisc jayDisc;
- @Test
- public void play() {
- jayDisc.play();
- }
- }
在ComponentScan掃描的包中,所有帶有@Component註解的類都會建立為bean
為元件掃描的bean命名
Spring應用上下文種所有的bean都會給定一個ID。在前面的例子中,儘管我們沒有明確地為JayDisc bean設定ID,但是Spring會預設為JayDisc設定ID為jayDisc,也就是將類名的第一個字母變成小寫。
如果想為這個bean設定不同的ID,那就將期望的值傳遞給@Component註解
- public class JayDisc implements ICompactDisc {
- ...
- }
如果不使用@Component註解的話,則使用Java依賴注入規範(Java Dependency Injection)中所提供的@Named註解bean的ID。
需要引入:
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- <version>1</version>
- </dependency>
- public class JayDisc implements ICompactDisc {
- ....
- }
設定元件掃描的基礎包
前面再給CDPlayerConfig類設定@ComponentScan,我們並沒有設定任何屬性,這個時候預設掃描預設包是:CDPlayerConfig類所在包及其包的子包。
如果是下圖這種情況,DisConfig與其這時候就需要設定@ComponentScan的掃描的包。
- public class DiscConfig {
- }
basePackages使用的是複數,則意味著可以設定多個基礎包。
但是basePackages後面跟的是String型別,這種型別並不安全。可以使用basePackageClasses有下面這種寫法:
- @Configuration
- @ComponentScan(basePackageClasses = {com.stalkers.soundsystem.JayCompactDisc.class})
- public class DiscConfig {
- }
通過為bean添加註解實現自動裝配
如果所有的物件都是獨立的,彼此之間沒有任何依賴,那麼使用元件掃描就能自動化裝配bean。
但是實際工作中,很多物件會依賴其他物件完成任務。這時候就需要能夠將元件掃描得到的bean和他們依賴裝配在一起。這就是自動裝配(autowiring)
使用Spring的Autowired
- public interface IMediaPlayer {
- void play();
- }
- public class CDPlayer implements IMediaPlayer {
- private ICompactDisc cd;
- public CDPlayer(ICompactDisc cd) {
- this.cd = cd;
- }
- public void play() {
- System.out.println("cd Play:");
- cd.play();
- }
- }
CDPlayer類的構造器上添加了@Autowired註解,表明當Spring建立CDPlayerbean的時候,會通過這個構造器來進行例項化
Autowired的多種方式
1.構造器註解(constructor)
2.屬性setter註解
3.field註解
不管使用上面3中的哪個方法,Spring都會滿足宣告的依賴。假如有且只有一個bean匹配依賴的話,那麼這個bean將會被裝配進來。
如果使用2,3方式註解,有多個bean的話,則用Qualifier指定。
如果沒有匹配的bean,那麼在應用上下文建立的時候,Spring會丟擲一個異常。為了避免異常的出現,可以使用
- private IMediaPlayer CDPlayer;
required=false表示如果沒有匹配的話,Spring會讓bean處於未裝配的樣子。使用未裝配的屬性,會出現NullPointerException
總結:
所以在使用開發的時候一般建議使用Resource(package javax.annotation)進行註解。但是Resource不支援構造器註解
二、通過Java程式碼裝配Bean
儘管在很多場景下通過元件掃描和自動裝配實現Spring的自動化更為推薦,但是有時候行不通。比如引用第三方元件,沒辦法在它的類上新增@Component及@Autowired。所以就需要JavaConfig或者XML配置
在進行顯示配置的時候,JavaConfig是更好的解決方案。
JavaConfig與其他的Java程式碼又有所區別,在概念上它與應用程式中的業務邏輯和領域程式碼又有所不同。JavaConfig是配置相關程式碼,不含任何邏輯程式碼。通常會將JavaConfig放到單獨的包中。
建立JavaConfig類
- public class CDPlayerConfig {
- }
使用@Configuration表明CDPlayerConfig是一個配置類
宣告簡單的bean
- public IMediaPlayer cdplayer() {
- return new VCDPlayer(new JayCompactDisc());
- }
@Bean註解會告訴Spring將返回一個物件。
預設情況下,@Bean的Id與帶有@Bean的方法名一樣。當然也可以通過@Bean的name屬性指定額外的方法名。
藉助JavaConfig注入
在上面的例子中,初始化個VCDPlayer都需要new一個JayCompactDisc物件。如果其他的物件的也需要JayCompactDisc,所以優化如下:
- public IMediaPlayer cdplayer() {
- return new VCDPlayer(disc());
- }
- public ICompactDisc disc() {
- return new JayCompactDisc();
- }
單獨抽出disc()方法,在其方法上加上Bean註解,Spring上加@Bean註解的都是預設單例模式,不管disc()被多個方法呼叫,其disc()都是同一個例項。
當然上面的初始化可以優化如下:
- public IMediaPlayer cdplayer(ICompactDisc disc) {
- return new VCDPlayer(disc);
- }
三、通過XML裝配Bean
在xml配置中,建立一個xml檔案,並且要以元素為根。
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd">
- </beans>
在使用xml的時候,需要在配置檔案頂部宣告多個xml模式(XML Schema Definition xsd)檔案
對於我們需要配置bean的則在spring-beans模式中。
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
- </beans>
1.藉助構造器注入初始化bean
構造器注入的方案:
1.元素
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
- <constructor-arg ref="jayCompactDisc"/>
- </bean>
- </beans>
2.使用Spring3.0所引入的c-名稱空間
使用c-名稱空間,需要引入:
xmlns:c="http://www.springframework.org/schema/c"
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:c="http://www.springframework.org/schema/c"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:cd-ref="jayCompactDisc">
- </bean>
- </beans>
解析:c-名稱空間的語法:
c:cd-ref="jayCompactDisc"
1.c 代表名稱空間字首
2.cd 代表VCDPlayer類的構造器引數名。當然我們也可以使用引數在整個引數列表的位置c:_0-ref
<bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="jayCompactDisc">
使用下劃線因為引數不能以數字開頭,所以加下劃線。
3.-ref 代表注入bean引用
4.jayCompactDisc 要注入的bean的id
注意:
c-命名需要寫在標籤內,與constructor-arg寫法差別很大
將字面量注入到構造器中
上面我們所做的DI通常指的是型別的裝配,也就是將物件的引用裝配到依賴他們的其他物件中,但是有時候我們傳的只是一個字面量值
- public class VaeCompactDisc implements ICompactDisc {
- private String title;
- public VaeCompactDisc(String title) {
- this.title = title;
- }
- public void play() {
- System.out.println("大家好,我是Vae,下面這首:" + title + "獻給大家的");
- }
- }
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
- </bean>
- <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
- <constructor-arg value="淺唱"></constructor-arg>
- </bean>
c-名稱空間的寫法
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
- </bean>
- <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="城府">
- <!--<constructor-arg value="淺唱"></constructor-arg>-->
- </bean>
裝配集合
- public class VaeCompactDisc implements ICompactDisc {
- private String title;
- private List<String> tracks;
- public VaeCompactDisc(String title, List<String> tracks) {
- this.title = title;
- this.tracks = tracks;
- }
- public void play() {
- System.out.println("大家好,我是Vae,下面這專輯:" + title + "獻給大家的");
- for (String s : tracks) {
- System.out.println(s);
- }
- }
- }
Spring配置使用constructor-arg。而c-命名的是無法使用裝配集合的功能
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
- </bean>
- <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
- <constructor-arg name="title" value="自定義"></constructor-arg>
- <constructor-arg name="tracks">
- <list>
- <value>有何不可</value>
- <value>多餘的解釋</value>
- </list>
- </constructor-arg>
- </bean>
2.使用屬性Setter方法注入
- public class CDPlayer implements IMediaPlayer {
- private ICompactDisc cd;
- public void setCd(ICompactDisc cd) {
- this.cd = cd;
- }
- public CDPlayer(ICompactDisc cd) {
- this.cd = cd;
- }
- public void play() {
- System.out.println("cd Play:");
- cd.play();
- }
- }
Spring.xml配置裡面
- <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
- <property name="cd" ref="jayCompactDisc"></property>
- </bean>
元素為屬性的Setter方法所提供的功能與元素為構造器所提供的功能是一樣的。
與c-名稱空間的類似的作為property的替代方案:p-名稱空間。使用p-名稱空間需要引入:
xmlns:p="http://www.springframework.org/schema/p"
Spring.xml配置如下
<bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" p:cd-ref="vaeCompactDisc">
語法解析:
p:cd-ref="vaeCompactDisc"
1.p-:名稱空間的字首
2.cd:屬性名稱
3.-ref:注入bean引用
4.vaeCompactDisc:所注入的bean的id
將字面量注入到屬性中
字面量注入到屬性與上面將字面量注入到構造方法中方式一樣。只不過標籤名改成了property。
裝配list也是與上面的構造器的裝配list一樣。
雖然我們無法使用c-及p-名稱空間裝配list,但是我們可以使用
- <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="自定義" c:tracks-ref="songs">
- </bean>
- <util:list id="songs">
- <value>有何不可</value>
- <value>多餘的解釋</value>
- </util:list>
Spring util名稱空間的中的元素:
元素 | 描述 |
---|---|
util:constant | 引用某個型別的public static 域 |
util:list | 建立一個java.util.List型別的bean,其中包含值或引用 |
util:map | 建立一個java.util.Map型別的bean,其中包含值或引用 |
util:properties | 建立一個java.util.Properties型別的bean |
util:property-path | 引用一個bean的屬性 |
util: set | 建立一個java.util.Set型別的bean |
四、匯入和混合配置
在Spring應用中,我們可以同時使用自動化和顯示配置。
如果一個JavaConfig配置太臃腫,我們可以把其進行拆分,然後使用@Import將拆分的類進行組合。
如果希望在JavaConfig裡引用xml配置。則可以使用@ImportResource