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.
 */
@Component
public class JayDisc implements ICompactDisc {

    private String title = "星晴";

    public void play() {
        System.out.println(title + ":一步兩步三步四步,望着天上星星...");
    }
}

Component注解作用:
表明該類會作為組件類。

不過,組件掃描默認是不開啟用的,我們還需要顯示配置下Spring,從而命令它去尋找帶有@Component注解的類,並為其創建bean。

1.java code開啟組件掃描:
其中,如果CompoentScan后面沒有參數的話,默認會掃描與配置類相同的包

@Configuration
@ComponentScan
public class CDPlayerConfig {
    @Bean
    public ICompactDisc disc() {
        return new JayDisc();
    }
}

2.xml啟動組件掃描

<?xml version="1.0" encoding="utf-8" ?>
<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注解

@Component("zhoujielun")
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>

@Named("zhoujielun")
public class JayDisc implements ICompactDisc {
  ....
}

設置組件掃描的基礎包

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

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

image

@Configuration
@ComponentScan(basePackages = {"com.stalkers.soundsystem"})
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();
}
@Component
public class CDPlayer implements IMediaPlayer {

    private ICompactDisc cd;
    
    @Autowired
    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會拋出一個異常。為了避免異常的出現,可以使用

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

@Configuration
public class CDPlayerConfig {
}

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

聲明簡單的bean

@Bean
public IMediaPlayer cdplayer() {
    return new VCDPlayer(new JayCompactDisc());
}

@Bean注解會告訴Spring將返回一個對象。

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

借助JavaConfig注入

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

@Bean
public IMediaPlayer cdplayer() {
    return new VCDPlayer(disc());
}

@Bean
public ICompactDisc disc() {
    return new JayCompactDisc();
}

單獨抽出disc()方法,在其方法上加上Bean注解,Spring上加@Bean注解的都是默認單例模式,不管disc()被多個方法調用,其disc()都是同一個實例。

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

@Bean
public IMediaPlayer cdplayer(ICompactDisc disc) {
    return new VCDPlayer(disc);
}

三、通過XML裝配Bean

在xml配置中,創建一個xml文件,並且要以 元素為根。

<?xml version="1.0" encoding="utf-8" ?>
<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模式中。

<?xml version="1.0" encoding="utf-8" ?>
<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. 元素

<?xml version="1.0" encoding="utf-8" ?>
<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"
<?xml version="1.0" encoding="utf-8" ?>
<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;

    @Autowired
    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,但是我們可以使用util: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM