Spring(3)——裝配 Spring Bean 詳解

裝配 Bean 的概述

前面已經介紹了 Spring IoC 的理念和設計,這一篇文章將介紹的是如何將本身開發的 Bean 裝配到 Spring IoC 容器中。html

大部分場景下,咱們都會使用 ApplicationContext 的具體實現類,由於對應的 Spring IoC 容器功能相對強大。java

而在 Spring 中提供了 3 種方法進行配置:git

  • 在 XML 文件中顯式配置
  • 在 Java 的接口和類中實現配置
  • 隱式 Bean 的發現機制和自動裝配原則

方式選擇的原則

在現實的工做中,這 3 種方式都會被用到,而且在學習和工做之中經常混合使用,因此這裏給出一些關於這 3 種優先級的建議:github

1.最優先:經過隱式 Bean 的發現機制和自動裝配的原則。
基於約定因爲配置的原則,這種方式應該是最優先的web

  • 好處:減小程序開發者的決定權,簡單又不失靈活。

2.其次:Java 接口和類中配置實現配置
在沒有辦法使用自動裝配原則的狀況下應該優先考慮此類方法正則表達式

  • 好處:避免 XML 配置的泛濫,也更爲容易。
  • 典型場景:一個父類有多個子類,好比學生類有兩個子類,一個男學生類和女學生類,經過 IoC 容器初始化一個學生類,容器將沒法知道使用哪一個子類去初始化,這個時候可使用 Java 的註解配置去指定。

3.最後:XML 方式配置
在上述方法都沒法使用的狀況下,那麼也只能選擇 XML 配置的方式。spring

  • 好處:簡單易懂(固然,特別是對於初學者)
  • 典型場景:當使用第三方類的時候,有些類並非咱們開發的,咱們沒法修改裏面的代碼,這個時候就經過 XML 的方式配置使用了。

經過 XML 配置裝配 Bean

使用 XML 裝配 Bean 須要定義對應的 XML,這裏須要引入對應的 XML 模式(XSD)文件,這些文件會定義配置 Spring Bean 的一些元素,當咱們在 IDEA 中建立 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>

這就只是一個格式文件,引入了一個 beans 的定義,引入了 xsd 文件,它是一個根元素,這樣它所定義的元素將能夠定義對應的 Spring Bean微信

裝配簡易值

先來一個最簡單的裝配:

<bean id="c" class="pojo.Category">
    <property name="name" value="測試" />
</bean>

簡單解釋一下:

  • id 屬性是 Spring 能找到當前 Bean 的一個依賴的編號,遵照 XML 語法的 ID 惟一性約束。必須以字母開頭,可使用字母、數字、連字符、下劃線、句號、冒號不能以 / 開頭
    不過 id 屬性不是一個必需的屬性name 屬性也能夠定義 bean 元素的名稱,能以逗號或空格隔開起多個別名,而且能夠使用不少的特殊字符,好比在 Spring 和 Spring MVC 的整合中,就得使用 name 屬性來定義 bean 的名稱,而且使用 / 開頭。
    注意: 從 Spring 3.1 開始,id 屬性也能夠是 String 類型了,也就是說 id 屬性也可使用 / 開頭,而 bean 元素的 id 的惟一性由容器負責檢查。
    若是 idname 屬性都沒有聲明的話,那麼 Spring 將會採用 「全限定名#{number}」 的格式生成編號。 例如這裏,若是沒有聲明 「id="c"」 的話,那麼 Spring 爲其生成的編號就是 「pojo.Category#0」,當它第二次聲明沒有 id 屬性的 Bean 時,編號就是 「pojo.Category#1」,以此類推。
  • class 屬性顯然就是一個類的全限定名
  • property 元素是定義類的屬性,其中的 name 屬性定義的是屬性的名稱,而 value 是它的值。

這樣的定義很簡單,可是有時候須要注入一些自定義的類,好比以前飲品店的例子,JuickMaker 須要用戶提供原料信息才能完成 juice 的製做:

<!-- 配置 srouce 原料 -->
<bean name="source" class="pojo.Source">
    <property name="fruit" value="橙子"/>
    <property name="sugar" value="多糖"/>
    <property name="size" value="超大杯"/>
</bean>

<bean name="juickMaker" class="pojo.JuiceMaker">
    <!-- 注入上面配置的id爲srouce的Srouce對象 -->
    <property name="source" ref="source"/>
</bean>

這裏先定義了一個 name 爲 source 的 Bean,而後再製造器中經過 ref 屬性去引用對應的 Bean,而 source 正是以前定義的 Bean 的 name ,這樣就能夠相互引用了。

  • 注入對象:使用 ref 屬性

裝配集合

有些時候咱們須要裝配一些複雜的東西,好比 Set、Map、List、Array 和 Properties 等,爲此咱們在 Packge【pojo】下新建一個 ComplexAssembly 類:

package pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class ComplexAssembly {
    
    private Long id;
    private List<String> list;
    private Map<String, String> map;
    private Properties properties;
    private Set<String> set;
    private String[] array;

    /* setter and getter */
}

這個 Bean 沒有任何的實際意義,知識爲了介紹如何裝配這些經常使用的集合類:

<bean id="complexAssembly" class="pojo.ComplexAssembly">
    <!-- 裝配Long類型的id -->
    <property name="id" value="1"/>
    
    <!-- 裝配List類型的list -->
    <property name="list">
        <list>
            <value>value-list-1</value>
            <value>value-list-2</value>
            <value>value-list-3</value>
        </list>
    </property>
    
    <!-- 裝配Map類型的map -->
    <property name="map">
        <map>
            <entry key="key1" value="value-key-1"/>
            <entry key="key2" value="value-key-2"/>
            <entry key="key3" value="value-key-2"/>
        </map>
    </property>
    
    <!-- 裝配Properties類型的properties -->
    <property name="properties">
        <props>
            <prop key="prop1">value-prop-1</prop>
            <prop key="prop2">value-prop-2</prop>
            <prop key="prop3">value-prop-3</prop>
        </props>
    </property>
    
    <!-- 裝配Set類型的set -->
    <property name="set">
        <set>
            <value>value-set-1</value>
            <value>value-set-2</value>
            <value>value-set-3</value>
        </set>
    </property>
    
    <!-- 裝配String[]類型的array -->
    <property name="array">
        <array>
            <value>value-array-1</value>
            <value>value-array-2</value>
            <value>value-array-3</value>
        </array>
    </property>
</bean>
  • 總結:
  • List 屬性爲對應的 <list> 元素進行裝配,而後經過多個 <value> 元素設值
  • Map 屬性爲對應的 <map> 元素進行裝配,而後經過多個 <entry> 元素設值,只是 entry 包含一個鍵值對(key-value)的設置
  • Properties 屬性爲對應的 <properties> 元素進行裝配,經過多個 <property> 元素設值,只是 properties 元素有一個必填屬性 key ,而後能夠設置值
  • Set 屬性爲對應的 <set> 元素進行裝配,而後經過多個 <value> 元素設值
  • 對於數組而言,可使用 <array> 設置值,而後經過多個 <value> 元素設值。

上面看到了對簡單 String 類型的各個集合的裝載,可是有些時候可能須要更爲複雜的裝載,好比一個 List 能夠是一個系列類的對象,爲此須要定義注入的相關信息,其實跟上面的配置沒什麼兩樣,只不過加入了 ref 這一個屬性而已:

  • 集合注入總結:
  • List 屬性使用 <list> 元素定義注入,使用多個 <ref> 元素的 Bean 屬性去引用以前定義好的 Bean
<property name="list">
    <list>
        <ref bean="bean1"/>
        <ref bean="bean2"/>
    </list>
</property>
  • Map 屬性使用 <map> 元素定義注入,使用多個 <entry> 元素的 key-ref 屬性去引用以前定義好的 Bean 做爲鍵,而用 value-ref 屬性引用以前定義好的 Bean 做爲值
<property name="map">
    <map>
        <entry key-ref="keyBean" value-ref="valueBean"/>
    </map>
</property>
  • Set 屬性使用 <set> 元素定義注入,使用多個 <ref> 元素的 bean 去引用以前定義好的 Bean
<property name="set">
    <set>
        <ref bean="bean"/>
    </set>
</property>

命名空間裝配

除了上述的配置以外, Spring 還提供了對應的命名空間的定義,只是在使用命名空間的時候要先引入對應的命名空間和 XML 模式(XSD)文件。

——【① c-命名空間】——

c-命名空間是在 Spring 3.0 中引入的,它是在 XML 中更爲簡潔地描述構造器參數的方式,要使用它的話,必需要在 XML 的頂部聲明其模式:

  • 注意:是經過構造器參數的方式

如今假設咱們如今有這麼一個類:

package pojo;

public class Student {

    int id;
    String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    // setter and getter
}

在 c-命名空間和模式聲明以後,咱們就可使用它來聲明構造器參數了:

<!-- 引入 c-命名空間以前 -->
<bean name="student1" class="pojo.Student">
    <constructor-arg name="id" value="1" />
    <constructor-arg name="name" value="學生1"/>
</bean>

<!-- 引入 c-命名空間以後 -->
<bean name="student2" class="pojo.Student"
      c:id="2" c:name="學生2"/>

c-命名空間屬性名以 「c:」 開頭,也就是命名空間的前綴。接下來就是要裝配的構造器參數名,在此以後若是須要注入對象的話則要跟上 -ref(如c:card-ref="idCard1",則對 card 這個構造器參數注入以前配置的名爲 idCard1 的 bean)

很顯然,使用 c-命名空間屬性要比使用 <constructor-arg> 元素精簡,而且會直接引用構造器之中參數的名稱,這有利於咱們使用的安全性。

咱們有另一種替代方式:

<bean name="student2" class="pojo.Student"
      c:_0="3" c:_1="學生3"/>

咱們將參數的名稱替換成了 「0」 和 「1」 ,也就是參數的索引。由於在 XML 中不容許數字做爲屬性的第一個字符,所以必需要添加一個下劃線來做爲前綴。

——【② p-命名空間】——

c-命名空間經過構造器注入的方式來配置 bean,p-命名空間則是用setter的注入方式來配置 bean ,一樣的,咱們須要引入聲明:

而後咱們就能夠經過 p-命名空間來設置屬性:

<!-- 引入p-命名空間以前 -->
<bean name="student1" class="pojo.Student">
    <property name="id" value="1" />
    <property name="name" value="學生1"/>
</bean>

<!-- 引入p-命名空間以後 -->
<bean name="student2" class="pojo.Student" 
      p:id="2" p:name="學生2"/>

咱們須要先刪掉 Student 類中的構造函數,否則 XML 約束會提示咱們配置 <constructor-arg> 元素。

一樣的,若是屬性須要注入其餘 Bean 的話也能夠在後面跟上 -ref

<bean name="student2" class="pojo.Student"
          p:id="2" p:name="學生2" p:cdCard-ref="cdCard1"/>
——【③ util-命名空間】——

工具類的命名空間,能夠簡化集合類元素的配置,一樣的咱們須要引入其聲明(無需擔憂怎麼聲明的問題,IDEA會有很友好的提示):

咱們來看看引入先後的變化:

<!-- 引入util-命名空間以前 -->
<property name="list">
    <list>
        <ref bean="bean1"/>
        <ref bean="bean2"/>
    </list>
</property>

<!-- 引入util-命名空間以後 -->
<util:list id="list">
    <ref bean="bean1"/>
    <ref bean="bean2"/>
</util:list>

<util:list> 只是 util-命名空間中的多個元素之一,下表提供了 util-命名空間提供的全部元素:

元素 描述
<util:constant> 引用某個類型的 public static 域,並將其暴露爲 bean
<util:list> 建立一個 java.util.List 類型的 bean,其中包含值或引用
<util:map> 建立一個 java.util.map 類型的 bean,其中包含值或引用
<util:properties> 建立一個 java.util.Properties 類型的 bean
<util:property-path> 引用一個 bean 的屬性(或內嵌屬性),並將其暴露爲 bean
<util:set> 建立一個 java.util.Set 類型的 bean,其中包含值或引用

引入其餘配置文件

在實際開發中,隨着應用程序規模的增長,系統中 <bean> 元素配置的數量也會大大增長,致使 applicationContext.xml 配置文件變得很是臃腫難以維護。

  • 解決方案:讓 applicationContext.xml 文件包含其餘配置文件便可
    使用 <import> 元素引入其餘配置文件

1.在【src】文件下新建一個 bean.xml 文件,寫好基礎的約束,把 applicationContext.xml 文件中配置的 <bean> 元素複製進去

2.在 applicationContext.xml 文件中寫入:

<import resource="bean.xml" />

3.運行測試代碼,仍然能正確獲取到 bean:


經過註解裝配 Bean

上面,咱們已經瞭解瞭如何使用 XML 的方式去裝配 Bean,可是更多的時候已經再也不推薦使用 XML 的方式去裝配 Bean,更多的時候回考慮使用註解(annotation) 的方式去裝配 Bean。

  • 優點:
    1.能夠減小 XML 的配置,當配置項多的時候,臃腫難以維護
    2.功能更增強大,既能實現 XML 的功能,也提供了自動裝配的功能,採用了自動裝配後,程序猿所須要作的決斷就少了,更加有利於對程序的開發,這就是「約定因爲配置」的開發原則

在 Spring 中,它提供了兩種方式來讓 Spring IoC 容器發現 bean:

  • 組件掃描:經過定義資源的方式,讓 Spring IoC 容器掃描對應的包,從而把 bean 裝配進來。
  • 自動裝配:經過註解定義,使得一些依賴關係能夠經過註解完成。

使用@Compoent 裝配 Bean

咱們把以前建立的 Student 類改一下:

package pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "student1")
public class Student {

    @Value("1")
    int id;
    @Value("student_name_1")
    String name;

    // getter and setter
}

解釋一下:

  • @Component註解:
    表示 Spring IoC 會把這個類掃描成一個 bean 實例,而其中的 value 屬性表明這個類在 Spring 中的 id,這就至關於在 XML 中定義的 Bean 的 id:<bean id="student1" class="pojo.Student" />,也能夠簡寫成 @Component("student1"),甚至直接寫成 @Component ,對於不寫的,Spring IoC 容器就默認以類名來命名做爲 id,只不過首字母小寫,配置到容器中。
  • @Value註解:
    表示值的注入,跟在 XML 中寫 value 屬性是同樣的。

這樣咱們就聲明好了咱們要建立的一個 Bean,就像在 XML 中寫下了這樣一句話:

<bean name="student1" class="pojo.Student">
    <property name="id" value="1" />
    <property name="name" value="student_name_1"/>
</bean>

可是如今咱們聲明瞭這個類,並不能進行任何的測試,由於 Spring IoC 並不知道這個 Bean 的存在,這個時候咱們可使用一個 StudentConfig 類去告訴 Spring IoC :

package pojo;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class StudentConfig {
}

這個類十分簡單,沒有任何邏輯,可是須要說明兩點:

  • 該類和 Student 類位於同一包名下
  • @ComponentScan註解:
    表明進行掃描,默認是掃描當前包的路徑,掃描全部帶有 @Component 註解的 POJO。

這樣一來,咱們就能夠經過 Spring 定義好的 Spring IoC 容器的實現類——AnnotationConfigApplicationContext 去生成 IoC 容器了:

ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
Student student = (Student) context.getBean("student1", Student.class);
student.printInformation();

這裏能夠看到使用了 AnnotationConfigApplicationContext 類去初始化 Spring IoC 容器,它的配置項是 StudentConfig 類,這樣 Spring IoC 就會根據註解的配置去解析對應的資源,來生成 IoC 容器了。

  • 明顯的弊端:
  • 對於 @ComponentScan 註解,它只是掃描所在包的 Java 類,可是更多的時候咱們但願的是能夠掃描咱們指定的類
  • 上面的例子只是注入了一些簡單的值,測試發現,經過 @Value 註解並不能注入對象

@Component 註解存在着兩個配置項:

  • basePackages:它是由 base 和 package 兩個單詞組成的,而 package 仍是用了複數,意味着它能夠配置一個 Java 包的數組,Spring 會根據它的配置掃描對應的包和子包,將配置好的 Bean 裝配進來
  • basePackageClasses:它由 base、package 和 class 三個單詞組成,採用複數,意味着它能夠配置多個類, Spring 會根據配置的類所在的包,爲包和子包進行掃描裝配對應配置的 Bean

咱們來試着重構以前寫的 StudentConfig 類來驗證上面兩個配置項:

package pojo;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "pojo")
public class StudentConfig {
}

//  —————————————————— 【 宇宙超級無敵分割線】—————————————————— 
package pojo;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackageClasses = pojo.Student.class)
public class StudentConfig {
}

驗證都能經過,bingo!

  • 對於 【basePackages】 和 【basePackageClasses】 的選擇問題:
    【basePackages】 的可讀性會更好一些,因此在項目中會優先選擇使用它,可是在須要大量重構的工程中,儘可能不要使用【basePackages】,由於不少時候重構修改包名須要反覆地配置,而 IDE 不會給你任何的提示,而採用【basePackageClasses】會有錯誤提示。

自動裝配——@Autowired

上面提到的兩個弊端之一就是沒有辦法注入對象,經過自動裝配咱們將解決這個問題。

所謂自動裝配技術是一種由 Spring 本身發現對應的 Bean,自動完成裝配工做的方式,它會應用到一個十分經常使用的註解 @Autowired 上,這個時候 Spring 會根據類型去尋找定義的 Bean 而後將其注入,聽起來很神奇,讓咱們實際來看一看:

1.先在 Package【service】下建立一個 StudentService 接口:

package service;

public interface StudentService {
    public void printStudentInfo();
}

使用接口是 Spring 推薦的方式,這樣能夠更爲靈活,能夠將定義和實現分離

2.爲上面的接口建立一個 StudentServiceImp 實現類:

package service;

import org.springframework.beans.factory.annotation.Autowired;
import pojo.Student;

@Component("studentService")
public class StudentServiceImp implements StudentService {

    @Autowired
    private Student student = null;

     // getter and setter

    public void printStudentInfo() {
        System.out.println("學生的 id 爲:" + student.getName());
        System.out.println("學生的 name 爲:" + student.getName());
    }
}

該實現類實現了接口的 printStudentInfo() 方法,打印出成員對象 student 的相關信息,這裏的 @Autowired 註解,表示在 Spring IoC 定位全部的 Bean 後,這個字段須要按類型注入,這樣 IoC 容器就會尋找資源,而後將其注入。

3.編寫測試類:

// 第一步:修改 StudentConfig 類,告訴 Spring IoC 在哪裏去掃描它:
package pojo;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = {"pojo", "service"})
public class StudentConfig {
}

// 或者也能夠在 XML 文件中聲明去哪裏作掃描
<context:component-scan base-package="pojo" />
<context:component-scan base-package="service" />

// 第二步:編寫測試類:
package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import pojo.StudentConfig;
import service.StudentService;
import service.StudentServiceImp;

public class TestSpring {

    public static void main(String[] args) {
        // 經過註解的方式初始化 Spring IoC 容器
        ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
        StudentService studentService = context.getBean("studentService", StudentServiceImp.class);
        studentService.printStudentInfo();
    }
}

運行代碼:

  • 再次理解: @Autowired 註解表示在 Spring IoC 定位全部的 Bean 後,再根據類型尋找資源,而後將其注入。
  • 過程: 定義 Bean ——》 初始化 Bean(掃描) ——》 根據屬性須要從 Spring IoC 容器中搜尋知足要求的 Bean ——》 知足要求則注入
  • 問題: IoC 容器可能會尋找失敗,此時會拋出異常(默認狀況下,Spring IoC 容器會認爲必定要找到對應的 Bean 來注入到這個字段,但有些時候並非必定須要,好比日誌)
  • 解決: 經過配置項 required 來改變,好比 @Autowired(required = false)

@Autowired 註解不只僅能配置在屬性之上,還容許方法配置,常見的 Bean 的 setter 方法也可使用它來完成注入,總之一切須要 Spring IoC 去尋找 Bean 資源的地方均可以用到,例如:

/* 包名和import */
public class JuiceMaker {
    ......
    @Autowired
    public void setSource(Source source) {
        this.source = source;
    }
}

在大部分的配置中都推薦使用這樣的自動注入來完成,這是 Spring IoC 幫助咱們自動裝配完成的,這樣使得配置大幅度減小,知足約定優於配置的原則,加強程序的健壯性。

自動裝配的歧義性(@Primary和@Qualifier)

在上面的例子中咱們使用 @Autowired 註解來自動注入一個 Source 類型的 Bean 資源,但若是咱們如今有兩個 Srouce 類型的資源,Spring IoC 就會不知所措,不知道究竟該引入哪個 Bean:

<bean name="source1" class="pojo.Source">
    <property name="fruit" value="橙子"/>
    <property name="sugar" value="多糖"/>
    <property name="size" value="超大杯"/>
</bean>
<bean name="source2" class="pojo.Source">
    <property name="fruit" value="橙子"/>
    <property name="sugar" value="少糖"/>
    <property name="size" value="小杯"/>
</bean>

咱們能夠會想到 Spring IoC 最底層的容器接口——BeanFactory 的定義,它存在一個按照類型獲取 Bean 的方法,顯然經過 Source.class 做爲參數沒法判斷使用哪一個類實例進行返回,這就是自動裝配的歧義性。

爲了消除歧義性,Spring 提供了兩個註解:

  • @Primary 註解:
    表明首要的,當 Spring IoC 檢測到有多個相同類型的 Bean 資源的時候,會優先注入使用該註解的類。
  • 問題:該註解只是解決了首要的問題,可是並無選擇性的問題
  • @Qualifier 註解:
    上面所談及的歧義性,一個重要的緣由是 Spring 在尋找依賴注入的時候是按照類型注入引發的。除了按類型查找 Bean,Spring IoC 容器最底層的接口 BeanFactory 還提供了按名字查找的方法,若是按照名字來查找和注入不就能消除歧義性了嗎?
  • 使用方法: 指定注入名稱爲 "source1" 的 Bean 資源
/* 包名和import */
public class JuiceMaker {
    ......
    @Autowired
    @Qualifier("source1")
    public void setSource(Source source) {
        this.source = source;
    }
}

使用@Bean 裝配 Bean

  • 問題: 以上都是經過 @Component 註解來裝配 Bean ,而且只能註解在類上,當你須要引用第三方包的(jar 文件),並且每每並無這些包的源碼,這時候將沒法爲這些包的類加入 @Component 註解,讓它們變成開發環境中的 Bean 資源。
  • 解決方案:
    1.本身建立一個新的類來擴展包裏的類,而後再新類上使用 @Component 註解,但這樣很 low
    2.使用 @Bean 註解,註解到方法之上,使其成爲 Spring 中返回對象爲 Spring 的 Bean 資源。

咱們在 Package【pojo】 下新建一個用來測試 @Bean 註解的類:

package pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanTester {

    @Bean(name = "testBean")
    public String test() {
        String str = "測試@Bean註解";
        return str;
    }
}
  • 注意: @Configuration 註解至關於 XML 文件的根元素,必需要,有了才能解析其中的 @Bean 註解

而後咱們在測試類中編寫代碼,從 Spring IoC 容器中獲取到這個 Bean :

// 在 pojo 包下掃描
ApplicationContext context = new AnnotationConfigApplicationContext("pojo");
// 由於這裏獲取到的 Bean 就是 String 類型因此直接輸出
System.out.println(context.getBean("testBean"));

@Bean 的配置項中包含 4 個配置項:

  • name: 是一個字符串數組,容許配置多個 BeanName
  • autowire: 標誌是不是一個引用的 Bean 對象,默認值是 Autowire.NO
  • initMethod: 自定義初始化方法
  • destroyMethod: 自定義銷燬方法

使用 @Bean 註解的好處就是可以動態獲取一個 Bean 對象,可以根據環境不一樣獲得不一樣的 Bean 對象。或者說將 Spring 和其餘組件分離(其餘組件不依賴 Spring,可是又想 Spring 管理生成的 Bean)

Bean 的做用域

在默認的狀況下,Spring IoC 容器只會對一個 Bean 建立一個實例,但有時候,咱們但願可以經過 Spring IoC 容器獲取多個實例,咱們能夠經過 @Scope 註解或者 <bean> 元素中的 scope 屬性來設置,例如:

// XML 中設置做用域
<bean id="" class="" scope="prototype" />
// 使用註解設置做用域
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Spring 提供了 5 種做用域,它會根據狀況來決定是否生成新的對象:

做用域類別 描述
singleton(單例) 在Spring IoC容器中僅存在一個Bean實例 (默認的scope)
prototype(多例) 每次從容器中調用Bean時,都返回一個新的實例,即每次調用getBean()時 ,至關於執行new XxxBean():不會在容器啓動時建立對象
request(請求) 用於web開發,將Bean放入request範圍 ,request.setAttribute("xxx") , 在同一個request 得到同一個Bean
session(會話) 用於web開發,將Bean 放入Session範圍,在同一個Session 得到同一個Bean
globalSession(全局會話) 通常用於 Porlet 應用環境 , 分佈式系統存在全局 session 概念(單點登陸),若是不是 porlet 環境,globalSession 等同於 Session

在開發中主要使用 scope="singleton"scope="prototype"對於MVC中的Action使用prototype類型,其餘使用singleton,Spring容器會管理 Action 對象的建立,此時把 Action 的做用域設置爲 prototype.

擴展閱讀:@Profile 註解條件化裝配 Bean

Spring 表達式語言簡要說明

Spring 還提供了更靈活的注入方式,那就是 Spring 表達式,實際上 Spring EL 遠比以上注入方式都要強大,它擁有不少功能:

  • 使用 Bean 的 id 來引用 Bean
  • 調用指定對象的方法和訪問對象的屬性
  • 進行運算
  • 提供正則表達式進行匹配
  • 集合配置

咱們來看一個簡單的使用 Spring 表達式的例子:

package pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("elBean")
public class ElBean {
    // 經過 beanName 獲取 bean,而後注入 
    @Value("#{role}")
    private Role role;
    
    // 獲取 bean 的屬性 id
    @Value("#{role.id}")
    private Long id;
    
    // 調用 bean 的 getNote 方法
    @Value("#{role.getNote().toString()}")
    private String note;
    /* getter and setter */
}

與屬性文件中讀取使用的 「$」 不一樣,在 Spring EL 中則使用 「#

擴展閱讀: Spring 表達式語言

參考資料:

  • 《Java EE 互聯網輕量級框架整合開發》
  • 《Java 實戰(第四版)》
  • 萬能的百度 and 萬能的大腦

歡迎轉載,轉載請註明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz 歡迎關注公衆微信號:wmyskxz_javaweb 分享本身的Java Web學習之路以及各類Java學習資料

相關文章
相關標籤/搜索