1. 程式人生 > 實用技巧 >Spring Bean詳細講解

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之間的依賴。

  1. package com.stalkers;
  2. /**
  3. * CD唱片介面
  4. * Created by stalkers on 2016/11/17.
  5. */
  6. public interface ICompactDisc {
  7. void play();
  8. }
  1. package com.stalkers.impl;
  2. import com.stalkers.ICompactDisc;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * Jay同名專輯
  6. * Created by stalkers on 2016/11/17.
  7. */
  8. @Component
  9. public class JayDisc implements ICompactDisc {
  10. private String title = "星晴";
  11. public void play() {
  12. System.out.println(title + ":一步兩步三步四步,望著天上星星...");
  13. }
  14. }

Component註解作用:
表明該類會作為元件類。

不過,元件掃描預設是不開啟用的,我們還需要顯示配置下Spring,從而命令它去尋找帶有@Component註解的類,併為其建立bean。

1.java code開啟元件掃描:
其中,如果CompoentScan後面沒有引數的話,預設會掃描與配置類相同的包

  1. @Configuration
  2. @ComponentScan
  3. public class CDPlayerConfig {
  4. @Bean
  5. public ICompactDisc disc() {
  6. return new JayDisc();
  7. }
  8. }

2.xml啟動元件掃描

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/context
  6. http://www.springframework.org/schema/context/spring-context.xsd">
  7. <context:component-scan base-package="com.stalkers.impl"/>
  8. </beans>

測試程式碼

  1. package com.stalkers;
  2. import com.stalkers.config.CDPlayerConfig;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.test.context.ContextConfiguration;
  7. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  8. /**
  9. * Created by stalkers on 2016/11/18.
  10. */
  11. @RunWith(SpringJUnit4ClassRunner.class)
  12. @ContextConfiguration(classes = CDPlayerConfig.class)
  13. public class TestPlay {
  14. @Autowired
  15. private ICompactDisc jayDisc;
  16. @Test
  17. public void play() {
  18. jayDisc.play();
  19. }
  20. }

在ComponentScan掃描的包中,所有帶有@Component註解的類都會建立為bean

為元件掃描的bean命名

Spring應用上下文種所有的bean都會給定一個ID。在前面的例子中,儘管我們沒有明確地為JayDisc bean設定ID,但是Spring會預設為JayDisc設定ID為jayDisc,也就是將類名的第一個字母變成小寫。

如果想為這個bean設定不同的ID,那就將期望的值傳遞給@Component註解

  1. @Component("zhoujielun")
  2. public class JayDisc implements ICompactDisc {
  3. ...
  4. }

如果不使用@Component註解的話,則使用Java依賴注入規範(Java Dependency Injection)中所提供的@Named註解bean的ID。

需要引入:

  1. <dependency>
  2. <groupId>javax.inject</groupId>
  3. <artifactId>javax.inject</artifactId>
  4. <version>1</version>
  5. </dependency>
  1. @Named("zhoujielun")
  2. public class JayDisc implements ICompactDisc {
  3. ....
  4. }

設定元件掃描的基礎包

前面再給CDPlayerConfig類設定@ComponentScan,我們並沒有設定任何屬性,這個時候預設掃描預設包是:CDPlayerConfig類所在包及其包的子包。

如果是下圖這種情況,DisConfig與其這時候就需要設定@ComponentScan的掃描的包。

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.stalkers.soundsystem"})
  3. public class DiscConfig {
  4. }

basePackages使用的是複數,則意味著可以設定多個基礎包。

但是basePackages後面跟的是String型別,這種型別並不安全。可以使用basePackageClasses有下面這種寫法:

  1. @Configuration
  2. @ComponentScan(basePackageClasses = {com.stalkers.soundsystem.JayCompactDisc.class})
  3. public class DiscConfig {
  4. }

通過為bean添加註解實現自動裝配

如果所有的物件都是獨立的,彼此之間沒有任何依賴,那麼使用元件掃描就能自動化裝配bean。

但是實際工作中,很多物件會依賴其他物件完成任務。這時候就需要能夠將元件掃描得到的bean和他們依賴裝配在一起。這就是自動裝配(autowiring)

使用Spring的Autowired

  1. public interface IMediaPlayer {
  2. void play();
  3. }
  1. @Component
  2. public class CDPlayer implements IMediaPlayer {
  3. private ICompactDisc cd;
  4. @Autowired
  5. public CDPlayer(ICompactDisc cd) {
  6. this.cd = cd;
  7. }
  8. public void play() {
  9. System.out.println("cd Play:");
  10. cd.play();
  11. }
  12. }

CDPlayer類的構造器上添加了@Autowired註解,表明當Spring建立CDPlayerbean的時候,會通過這個構造器來進行例項化

Autowired的多種方式
1.構造器註解(constructor)

2.屬性setter註解

3.field註解

不管使用上面3中的哪個方法,Spring都會滿足宣告的依賴。假如有且只有一個bean匹配依賴的話,那麼這個bean將會被裝配進來。

如果使用2,3方式註解,有多個bean的話,則用Qualifier指定。

如果沒有匹配的bean,那麼在應用上下文建立的時候,Spring會丟擲一個異常。為了避免異常的出現,可以使用

  1. @Autowired(required = false)
  2. 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類

  1. @Configuration
  2. public class CDPlayerConfig {
  3. }

使用@Configuration表明CDPlayerConfig是一個配置類

宣告簡單的bean

  1. @Bean
  2. public IMediaPlayer cdplayer() {
  3. return new VCDPlayer(new JayCompactDisc());
  4. }

@Bean註解會告訴Spring將返回一個物件。

預設情況下,@Bean的Id與帶有@Bean的方法名一樣。當然也可以通過@Bean的name屬性指定額外的方法名。

藉助JavaConfig注入

在上面的例子中,初始化個VCDPlayer都需要new一個JayCompactDisc物件。如果其他的物件的也需要JayCompactDisc,所以優化如下:

  1. @Bean
  2. public IMediaPlayer cdplayer() {
  3. return new VCDPlayer(disc());
  4. }
  5. @Bean
  6. public ICompactDisc disc() {
  7. return new JayCompactDisc();
  8. }

單獨抽出disc()方法,在其方法上加上Bean註解,Spring上加@Bean註解的都是預設單例模式,不管disc()被多個方法呼叫,其disc()都是同一個例項。

當然上面的初始化可以優化如下:

  1. @Bean
  2. public IMediaPlayer cdplayer(ICompactDisc disc) {
  3. return new VCDPlayer(disc);
  4. }

三、通過XML裝配Bean

在xml配置中,建立一個xml檔案,並且要以元素為根。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. </beans>

在使用xml的時候,需要在配置檔案頂部宣告多個xml模式(XML Schema Definition xsd)檔案

對於我們需要配置bean的則在spring-beans模式中。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
  7. </beans>

1.藉助構造器注入初始化bean

構造器注入的方案:
1.元素

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
  7. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
  8. <constructor-arg ref="jayCompactDisc"/>
  9. </bean>
  10. </beans>

2.使用Spring3.0所引入的c-名稱空間

使用c-名稱空間,需要引入:

xmlns:c="http://www.springframework.org/schema/c"
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:c="http://www.springframework.org/schema/c"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
  8. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:cd-ref="jayCompactDisc">
  9. </bean>
  10. </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通常指的是型別的裝配,也就是將物件的引用裝配到依賴他們的其他物件中,但是有時候我們傳的只是一個字面量值

  1. public class VaeCompactDisc implements ICompactDisc {
  2. private String title;
  3. public VaeCompactDisc(String title) {
  4. this.title = title;
  5. }
  6. public void play() {
  7. System.out.println("大家好,我是Vae,下面這首:" + title + "獻給大家的");
  8. }
  9. }
  1. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
  2. </bean>
  3. <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
  4. <constructor-arg value="淺唱"></constructor-arg>
  5. </bean>

c-名稱空間的寫法

  1. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
  2. </bean>
  3. <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="城府">
  4. <!--<constructor-arg value="淺唱"></constructor-arg>-->
  5. </bean>

裝配集合

  1. public class VaeCompactDisc implements ICompactDisc {
  2. private String title;
  3. private List<String> tracks;
  4. public VaeCompactDisc(String title, List<String> tracks) {
  5. this.title = title;
  6. this.tracks = tracks;
  7. }
  8. public void play() {
  9. System.out.println("大家好,我是Vae,下面這專輯:" + title + "獻給大家的");
  10. for (String s : tracks) {
  11. System.out.println(s);
  12. }
  13. }
  14. }

Spring配置使用constructor-arg。而c-命名的是無法使用裝配集合的功能

  1. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
  2. </bean>
  3. <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
  4. <constructor-arg name="title" value="自定義"></constructor-arg>
  5. <constructor-arg name="tracks">
  6. <list>
  7. <value>有何不可</value>
  8. <value>多餘的解釋</value>
  9. </list>
  10. </constructor-arg>
  11. </bean>

2.使用屬性Setter方法注入

  1. public class CDPlayer implements IMediaPlayer {
  2. private ICompactDisc cd;
  3. @Autowired
  4. public void setCd(ICompactDisc cd) {
  5. this.cd = cd;
  6. }
  7. public CDPlayer(ICompactDisc cd) {
  8. this.cd = cd;
  9. }
  10. public void play() {
  11. System.out.println("cd Play:");
  12. cd.play();
  13. }
  14. }

Spring.xml配置裡面

  1. <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
  2. <property name="cd" ref="jayCompactDisc"></property>
  3. </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,但是我們可以使用

  1. <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="自定義" c:tracks-ref="songs">
  2. </bean>
  3. <util:list id="songs">
  4. <value>有何不可</value>
  5. <value>多餘的解釋</value>
  6. </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