Spring詳解3.Bean的裝配


點擊進入個人博客

1 Spring容器與Bean配置信息

Bean配置信息

Bean配置信息是Bean的元數據信息,它由一下4個方面組成:java

  • Bean的實現類
  • Bean的屬性信息,如數據庫的鏈接數、用戶名、密碼。
  • Bean的依賴關係,Spring根據依賴關係配置完成Bean之間的裝配。
  • Bean的行爲信息,如生命週期範圍及生命週期各過程的回調函數。
Bean元數據信息

Bean元數據信息在Spring容器中的內部對應物是一個個BeanDefinition造成的Bean註冊表,Spring實現了Bean元數據信息內部表示和外部定義之間的解耦。正則表達式

Spring支持的配置方式

Spring1.0僅支持基於XML的配置,Spring2.0新增基於註解配置的支持,Spring3.0新增基於Java類配置的支持,Spring4.0則新增給予Groovy動態語言配置的支持。
Spring容器內部協做解構spring

2 基於XML的配置

2.1 理解XML與Schema

<?xml version="1.0" encoding="utf-8" ?>
<beans (1)xmlns="http://www.springframework.org/schema/beans"
       (2)xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:(3)context=(4)"http://www.springframework.org/schema/context"
       xsi:(5)schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
  • (1)處是默認命名空間,無命名空間前綴的元素屬於默認命名空間。
  • (2)xsi標準命名空間,用於指定自定義命名空間的Schema文件
  • (3)自定義命名空間的簡稱,能夠任意命名
  • (4)自定義命名空間的全稱,必須在xsi命名空間爲其指定空間對應的Schema文件,能夠任意命名,習慣上用文檔發佈機構的相關網站目錄。
  • (5)爲每一個命名空間指定Schema文件位置,
詳解xmlns
  • 定義:xml namespace的縮寫,可譯爲「XML命名空間」。
  • 做用:防止XML文檔含有相同的元素命名衝突,如<table>既能夠表示表格,又能夠表示桌子。若是增長了命名空間如<table>和<t:table>就可使二者區分開來。
  • 使用:xmlns:namespace-prefix="namespaceURI",其中namespace-prefix爲自定義前綴,只要在這個XML文檔中保證前綴不重複便可;namespaceURI是這個前綴對應的XML Namespace的定義。
理解xsi:schemaLocation

xsi:schemaLocation定義了XML Namespace和對應的 XSD(Xml Schema Definition)文檔的位置的關係。它的值由一個或多個URI引用對組成,兩個URI之間以空白符分隔(空格和換行都可)。第一個URI是定義的 XML Namespace的值,第二個URI給出Schema文檔的位置,Schema處理器將從這個位置讀取Schema文檔,該文檔的targetNamespace必須與第一個URI相匹配。例如:數據庫

<beans 
       xsi:schemaLocation="http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

這裏表示Namespace爲http://www.springframework.or...://www.springframework.org/schema/context/spring-context.xsd。express

2.2 使用XML裝配Bean

直接裝配Bean
<!--id不能夠配置多個: context.getBean("myBean1, myBean2")-->
<bean class="com.ankeetc.spring.MyBean" id="myBean1, myBean2"/>
    
<!--context.getBean("myBean1") == context.getBean("myBean2")-->
<bean class="com.ankeetc.spring.MyBean" name="myBean1, myBean2"/>
  • id:用於表示一個Bean的名稱,在容器內不能重複;不能夠配置多個id。
  • name:用於表示一個Bean的名稱,在容器內不能重複;能夠配置多個名稱,用,分割;id和name能夠都爲空,此時則能夠經過獲取全限定類名來獲取Bean。
  • class:全限定類名
靜態工廠方法裝配
  • 靜態工廠無需建立工廠類示例就能夠調用工廠類方法。
  • factory-method:工廠方法
public class MyBeanFactory {
    public static MyBean createMyBean() {
        return new MyBean();
    }
}
<bean id="myBean" class="com.ankeetc.spring.MyBeanFactory" factory-method="createMyBean"/>
非靜態工廠方法裝配
  • 非靜態工廠方法必須首先定義一個工廠類的Bean,而後經過factory-bean引用工廠類實例。
  • factory-bean:指向定義好的工廠Bean
public class MyBeanFactory {
    public MyBean createMyBean() {
        return new MyBean();
    }
}
<bean id="myBeanFactory" class="com.ankeetc.spring.MyBeanFactory"/>
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createMyBean"/>
Bean的繼承和依賴
  • parent:經過設置<bean>標籤的parent屬性,子<bean>將自動繼承父<bean>的配置信息。
  • depends-on:經過設置<bean>標籤的depends-on屬性,Spring容許顯示的設置當前Bean前置依賴的Bean,確保前置依賴的Bean在當前Bean實例化以前已經建立好。
自動裝配autowire

<beans>元素提供了一個default-autowire屬性能夠全局自動匹配,默認爲no。<bean>元素提供了一個指定自動裝配類型的autowire屬性,能夠覆蓋<beans>元素的default-autowire屬性,該屬性有以下選項:緩存

自動裝配類型 說明
no 顯式指定不使用自動裝配。
byName 若是存在一個和當前屬性名字一致的 Bean,則使用該 Bean 進行注入。若是名稱匹配可是類型不匹配,則拋出異常。若是沒有匹配的類型,則什麼也不作。
byType 若是存在一個和當前屬性類型一致的 Bean ( 相同類型或者子類型 ),則使用該 Bean 進行注入。byType 可以識別工廠方法,即可以識別 factory-method 的返回類型。若是存在多個類型一致的 Bean,則拋出異常。若是沒有匹配的類型,則什麼也不作。
constructor 與 byType 相似,只不過它是針對構造函數注入而言的。若是當前沒有與構造函數的參數類型匹配的 Bean,則拋出異常。使用該種裝配模式時,優先匹配參數最多的構造函數。
default 根據 Bean 的自省機制決定採用 byType 仍是 constructor 進行自動裝配。若是 Bean 提供了默認的構造函數,則採用 byType;不然採用 constructor 進行自動裝配。
經過util命名空間配置集合類型的Bean
<util:list></util:list>
<util:set></util:set>
<util:map></util:map>

2.3 使用XML依賴注入

屬性配置
  • Bean有一個無參數的構造器
  • 屬性有對應的Setter函數
  • 屬性命名知足JavaBean的屬性命名規範
<bean class="com.ankeetc.spring.MyBean" id="myBean">
    <property name="prop" value="prop"/>
</bean>
構造方法
  • constructor-arg中的type和index能夠沒有,只要能保證能夠惟一的肯定對應的構造方法便可
  • type中基本數據類型和對應的包裝類不能通用
  • 循環依賴:若是兩個Bean都採用構造方法注入,並且都經過構造方法入參引用對方,就會形成循環依賴致使死鎖。
<bean class="com.ankeetc.spring.MyBean" id="myBean">
    <constructor-arg type="java.lang.String" index="0" value="abc"/>
    <constructor-arg type="int" index="1" value="10"/>
</bean>

2.4 注入參數

字面值
  • 基本數據類型及其封裝類、String均可以採起字面值注入。
  • 特殊字符可使用<![CDATA[]]>節或者轉義序列
引用其餘Bean

<ref>元素能夠經過如下三個屬性引用容器中的其餘Bean:session

  • bean:經過該屬性能夠引用同一容器或父容器的Bean,這是最多見的形式。
  • local:經過該屬性只能引用同一配置文件中定義的Bean,它能夠利用XML解析器自動檢驗引用的合法性,以便在開發編寫配置時可以及時發現並糾正配置的錯誤。
  • parent:引用父容器中的Bean,如<ref parent="car">的配置說明car的Bean是父容器中的Bean。
內部Bean
  • 內部Bean只會被當前Bean引用,不會被容器中其餘的Bean引用
  • 內部Bean即便提供了id、name、scope也會被忽略,Scope默認爲prototype類型。
<bean id="prop" class="com.ankeetc.spring.Prop">
        <property name="value" value="1314"/>
    </bean>

    <bean id="myBean" class="com.ankeetc.spring.MyBean">
        <property name="prop">
            <!--內部Bean即便提供了id、name、scope也會被忽略-->
            <bean id="prop" class="com.ankeetc.spring.Prop">
                <property name="value" value="520"/>
            </bean>
        </property>
    </bean>
null值
  • 使用<null/>表明null值
級聯屬性
  • Spring支持級聯屬性如prop.value,並且支持多層級聯屬性
  • 級聯屬性必須有初始值,不然會拋出NullValueInNestedPathException
public class MyBean {
    // 必須初始化
    private Prop prop = new Prop();

    public Prop getProp() {
        return prop;
    }

    public void setProp(Prop prop) {
        this.prop = prop;
    }
}
<bean id="myBean" class="com.ankeetc.spring.MyBean">
        <property name="prop.value" value="1314"/>
    </bean>
集合類型屬性
  • List、Set、Map:經過<list><set><map><entry>等標籤能夠設置List、Set、Map的屬性
  • Properties:能夠經過<props><prop>等標籤設置Properties的屬性,Properties屬性的鍵值都只能是字符串。
  • 集合合併:子Bean能夠繼承父Bean的同名屬性集合元素,而且使用merge屬性選擇是否合併,默認不合並。
<bean id="parentBean" class="com.ankeetc.spring.MyBean">
        <property name="list">
            <list>
                <value>1314</value>
            </list>
        </property>
    </bean>

    <bean id="myBean" class="com.ankeetc.spring.MyBean" parent="parentBean">
        <property name="list">
            <list merge="true">
                <value>520</value>
            </list>
        </property>
    </bean>

2.5 多配置文件整合

  1. 能夠經過ApplicationContext加載多個配置文件,此時多個配置文件中的<bean>是能夠互相訪問的。
  2. 能夠經過XML中的<import>將多個配置文件引入到一個文件中,這樣只須要加載一個配置文件便可。

2.6 Bean的做用域

類型 說明
singleton 在Spring IoC容器中僅存在一個Bean實例,Bean以單實例的方式存在
prototype 每次從容器中調用Bean時,都返回一個新的實例
request 每次HTTP請求都會建立一個新的Bean,該做用域僅適用於WebApplicationContext環境
session 同一個HTTP session共享一個Bean,不一樣的HTTP session使用不一樣的Bean,該做用域僅適用於WebApplicationContext環境
globalSession 同一個全局Session共享一個Bean,通常用於Portlet環境,該做用域僅適用於WebApplicationContext環境
singleton做用域
  • 無狀態或者狀態不可變的類適合使用單例模式
  • 若是用戶不但願在容器啓動時提早實例化singleton的Bean,可使用lazy-init屬性進行控制
  • 若是該Bean被其餘須要提早實例化的Bean所引用,那麼Spring將會忽略lazy-init的設置
prototype做用域
  • 設置爲scope="prototype"以後,每次調用getBean()都會返回一個新的實例
  • 默認狀況下,容器在啓動時不會實例化prototype的Bean
  • Spring容器將prototype的Bean交給調用者後就再也不管理它的生命週期
Web應用環境相關的Bean做用域

見後續章節app

做用域依賴的問題

見後續章節ide

3 FactoryBean

因爲實例化Bean的過程比較負責,可能須要大量的配置,這是採用編碼的方式多是更好的選擇。Spring提供了FactoryBean工廠類接口,用戶能夠實現該接口定製實例化Bean的邏輯。當配置文件中<bean>的class屬性配置的是FactoryBean的子類時,經過getBean()返回的不是FactoryBean自己,而是getObject()方法所返回的對象,至關因而FactoryBean#getObject()代理了getBean()方法函數

  • T getObject() throws Exception;:返回由FactoryBean建立的Bean實例,若是isSingleton()返回的是true,該實例會放到Spring容器的實例緩存池中。
  • Class<?> getObjectType();:返回該FactoryBean建立的Bean的類型
  • boolean isSingleton();:建立的Bean是singleton的仍是prototype
/**
 * 實現,分割的方式配置 KFCCombo 屬性
 */
public class KFCFactoryBean implements FactoryBean<KFCCombo> {
    private String prop;

    public String getProp() {
        return prop;
    }

    // 接受,分割的屬性設置信息
    public void setProp(String prop) {
        this.prop = prop;
    }

    // 實例化KFCCombo
    public KFCCombo getObject() throws Exception {
        KFCCombo combo = new KFCCombo();
        String[] props = prop.split(",");
        combo.setBurger(props[0]);
        combo.setDrink(props[1]);
        return combo;
    }

    public Class<?> getObjectType() {
        return KFCCombo.class;
    }

    // true則放進容器緩存池,false則每次都調用getObject()方法返回新的對象
    public boolean isSingleton() {
        return false;
    }
}
<bean id="combo" class="com.ankeetc.spring.KFCFactoryBean">
        <property name="prop" value="ZingerBurger, PepsiCola"/>
    </bean>

4 基於註解的配置

4.1 支持的註解

@Component:在Bean的實現類上直接標註,能夠被Spring容器識別
@Repository:用於對DAO實現類進行標柱
@Service:用於對Service實現類進行標註
@Controller:用於對Controller實現類進行標註

4.2 掃描註解定義對Bean

Spring提供了一個context命名空間,用於掃描以註解定義Bean的類。

<!--生命context命名空間-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="com.ankeetc.spring"/>
</beans>
base-package屬性
  • 指定一個須要掃描的基類包,Spring容器會掃描這個包下的全部類,並提取標註了相關注解的Bean。
resource-pattern屬性
  • 若是不但願掃描base-package下的全部類,可使用該屬性提供過濾
  • 該屬性默認是**/*.class,即基包下的全部類
<context:exclude-filter>與<context:include-filter>
  • <context:exclude-filter>:表示要排除的目標類
  • <context:include-filter>:表示要包含的目標類
  • <context:component-scan>能夠有多個上述兩個子元素;首先根據exclude-filter列出須要排除的黑名單,而後再根據include-filter流出須要包含的白名單。
類別 示例 說明
annotation com.ankeetc.XxxAnnotation 全部標註了XxxAnnotation的類。該類型採用目標類是否標誌了某個註解進行過濾。
assignable com.ankeetc.XxService 全部繼承或擴展XXXService的類。該類型採用目標類是否繼承或者擴展了某個特定類進行過濾
aspectj com.ankeetc..*Service+ 全部類名以Service結束的類及繼承或者擴展他們的類。該類型採用AspectJ表達式進行過濾
regex com.ankeetc.auto..* 全部com.ankeetc.auto類包下的類。該類型採用正則表達式根據目標類的類名進行過濾
custom com.ankeetc.XxxTypeFilter 採用XxxTypeFilter代碼方式實現過濾規則,該類必須實現org.springframework.core.type.TypeFilter接口
use-default-filters屬性
  • use-default-filters屬性默認值爲true,表示會對標註@Component、@Controller、@Service、@Reposity的Bean進行掃描。
  • 若是想僅掃描一部分的註解,須要將該屬性設置爲false。
<!-- 僅掃描標註了@Controller註解的類-->
<context:component-scan base-package="com.ankeetc.spring" use-default-filters="false">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

4.3 自動裝配

@Component
public class KFCCombo {
    @Autowired
    private PepsiCola cola;

    @Autowired
    private Map<String, Cola> colaMap;

    @Autowired
    private List<ZingerBurger> burgerList;

    private ZingerBurger burger;
    @Autowired
    public void setBurger(@Qualifier(value = "zingerBurger") ZingerBurger burger) {
        this.burger = burger;
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:/beans.xml"});

        KFCCombo combo = context.getBean(KFCCombo.class);
    }
}

interface Cola {}

@Order(value = 1)
@Component
class CocaCola implements Cola {}

@Order(value = 2)
@Component
class PepsiCola implements Cola {}

@Component(value = "zingerBurger")
class ZingerBurger {
}
@Autowired註解
  • 使用該註解能夠按類型自動裝配對應的Bean
  • 沒有找到對應的Bean則會拋出NoSuchBeanDefinitionException異常
  • 使用required=false屬性能夠設置即便找不到對應的Bean(即爲null)也不會拋出異常
  • @Autowired能夠對類成員變量及方法入參進行標註
@Quaifiler
  • 若是容器中有一個以上匹配的Bean時,能夠按照Bean名字查找對應的Bean
  • @Quaifiler須要與@Autowired配合使用
對集合類進行標註
  • 可使用@Autowired對集合類進行標註,Spring會講容器中按類型匹配對全部Bean注入進來
  • 可使用@Order指定加載順序,值越小的越先加載
@Lazy延遲加載
  • 可使用@Lazy實現延遲加載,不會當即注入屬性值,而是延遲到調用此屬性對時候纔會注入屬性值。
@Resource和@Inject
  • Spring支持JSR-250中@Resource註解和JSR-330的@Inject註解
  • @Resource採用的是按照名稱加載的方式,它要求提供一個Bean名稱的屬性,若是屬性爲空,則自動採用標註處的變量名或方法名做爲Bean的名稱。
  • @Inject是按照類型匹配注入Bean的。
  • 因爲這兩個註解功能沒有@Autowired功能強大,通常不須要使用。

4.4 Bean做用範圍及生命週期

  • 註解配置的Bean默認做用範圍爲singleton,可使用@Scope顯示指定做用範圍
  • 可使用@PostConstruct和@PreDestroy註解來達到init-method和destroy-method屬性的功能。
  • @PostConstruct和@PreDestroy註解能夠有多個

5 基於Java類的配置

5.1 @Configuration註解

  • JavaConfig是Spring的一個子項目,旨在經過Java類的方式提供Bean的定義信息。
  • 普通的POJO標註了@Configuration註解,就能夠被Spring容器提供Bean定義信息。
  • @Configuration註解自己已經標註了@Component註解,因此任何標註了@Configuration的類均可以做爲普通的Bean。

5.2 @Bean註解

  • @Bean標註在方法上,用於產生一個Bean
  • Bean的類型由方法的返回值的類型肯定,Bean名稱默認與方法名相同,也能夠顯示指定Bean的名稱。
  • 可使用@Scope來控制Bean的做用範圍。

5.3 啓動Spring容器

經過@Configuration類啓動Spring容器
  1. 能夠直接設置容器啓動要註冊的類
  2. 能夠向容器中註冊新的類,註冊了新的類要記得refresh
  3. 能夠經過@Import將多個配置類組裝稱一個配置類
public class Main {
    public static void main(String[] args) {
        // (1)能夠直接設置容器啓動要加載的類
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DaoConfig.class);
        // (2)能夠向容器中註冊新的類
        ((AnnotationConfigApplicationContext) applicationContext).register(ServiceConfig.class);
        // (3)註冊了新的類要記得refresh
        ((AnnotationConfigApplicationContext) applicationContext).refresh();
    }
}

@Configuration
class DaoConfig {
    @Bean
    public String getStr() {
        return "1314";
    }
}

@Configuration
@Import(DaoConfig.class)
// (4)能夠經過@Import將多個配置類組裝稱一個配置類
class ServiceConfig {
}
經過XML配置文件引用@Configuration的配置

標註了@Configureation的配置類自己也是一個bean,它能夠被Spring的<context:component-scan>掃描到。若是但願將此配置類組裝到XML配置文件中,經過XML配置文件啓動Spring容器,僅在XML文件中經過<context:component-scan>掃描到相應的配置類便可。

<context:component-scan base-package="com.ankeetc.spring" resource-pattern="Config.class"/>
經過@Configuration配置類引用XML配置信息

在標註了@Configuration的配置類中,能夠經過@ImportResource引入XML配置文件。

@Configuration
@ImportResource("classpath:beans.xml")
public class Config {
}
相關文章
相關標籤/搜索