Spring學習之IOC中Bean的xml配置

一. IOC和DI的概念

IOC(Inversion of Control):反轉控制。
在應用程序中的組件需要獲取資源時,傳統的方式是組件主動的從容器中獲取所需要的資源,在這樣的模式下開發人員往往需要知道在具體容器中特定資源的獲取方式,增加了學習成本,同時降低了開發效率。

反轉控制的思想完全顛覆了應用程序組件獲取資源的傳統方式:反轉了資源的獲取方向——改由容器主動的將資源推送給需要的組件,開發人員不需要知道容器是如何創建資源對象的,只需要提供接收資源的方式即可,極大的降低了學習成本,提高了開發的效率。這種行爲也稱爲查找的被動形式。

DI(Dependency Injection):依賴注入。
IOC的另一種表述方式:即組件以一些預先定義好的方式(例如:setter 方法)接受來自於容器的資源注入。相對於IOC而言,這種表述更直接。

IOC容器在Spring中的實現

  1. 在通過IOC容器讀取Bean的實例之前,需要先將IOC容器本身實例化。

  2. Spring提供了IOC容器的兩種實現方式

    (1)BeanFactory:IOC容器的基本實現,是Spring內部的基礎設施,是面向Spring本身的,不是提供給開發人員使用的。
    (2)ApplicationContext:BeanFactory的子接口,提供了更多高級特性。面向Spring的使用者,幾乎所有場合都使用ApplicationContext而不是底層的BeanFactory。

  3. ApplicationContext的主要實現類

    [1]ClassPathXmlApplicationContext:對應類路徑下的XML格式的配置文件
    [2]FileSystemXmlApplicationContext:對應文件系統中的XML格式的配置文件
    [3]在初始化時就創建單例的bean,也可以通過配置的方式指定創建的Bean是多實例的。

  4. ConfigurableApplicationContext

    [1]是ApplicationContext的子接口,包含一些擴展方法
    [2]refresh()和close()讓ApplicationContext具有啓動、關閉和刷新上下文的能力。

  5. WebApplicationContext
    專門爲WEB應用而準備的,它允許從相對於WEB根目錄的路徑中完成初始化工作

二. Bean的賦值

  1. 通過setXxx()屬性賦值
    <bean id="person1" class="com.hello.Person">
        <!--  利用getter setter方法創建對象並賦值  -->
        <property name="lastName" value="張三"></property>
        <property name="age" value="28"></property>
        <property name="gender" value=""></property>
        <property name="email" value="[email protected]"></property>
    </bean>
  1. 通過多參數構造器賦值
    <bean id="person2" class="com.hello.Person">
        <!--  利用有參構造器創建對象並賦值  -->
        <constructor-arg name="lastName" value="李四"></constructor-arg>
        <constructor-arg name="age" value="23"></constructor-arg>
        <constructor-arg name="email" value="[email protected]"></constructor-arg>
        <constructor-arg name="gender" value=""></constructor-arg>

    </bean>
  1. 指定索引賦值
    <bean id="person3" class="com.hello.Person">
        <!--  name可以省略 但是順序要保持和構造器一樣,否則使用index指定順序 -->
        <constructor-arg value="王五"></constructor-arg>
        <constructor-arg value="23"></constructor-arg>
        <constructor-arg value="[email protected]" index="3"></constructor-arg>
        <constructor-arg value="" index="2"></constructor-arg>

    </bean>
  1. 指定類型
    </bean>
    <bean id="person4" class="com.hello.Person">
        <!-- 當出現有着相同參數個數的構造器,可以用 type指定類型  -->
        <constructor-arg value="小紅"></constructor-arg>
        <constructor-arg value="23" type="java.lang.Integer"></constructor-arg>
        <constructor-arg value="[email protected]"></constructor-arg>

    </bean>
  1. 通過p名稱空間賦值
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" 
       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">
         
    <!-- 通過p名稱空間爲bean賦值-->
    <bean id="person5" class="com.hello.Person"
          p:age="18" p:email="[email protected]" p:lastName="哈哈">

    </bean>
  1. 特殊值賦值(null, 特殊符號)
    <!-- 通過標籤<null/>表示null值-->
    <!-- 通過標籤<![CDATA[XXX]]>表示特殊符號XXX -->
    <bean id="person6" class="com.hello.Person">
        <property name="lastName" value="Tom"></property>
        <property name="gender"><null/></property>
        <property name="email">
            <value><![CDATA[%%%hehe%%%^]]></value>
        </property>
    </bean>
  1. 引用外部的值
    <bean id="car1" class="com.hello.Car">
        <property name="carName" value="寶馬"></property>
        <property name="color" value="Grenn"></property>
        <property name="price" value="200000.0"></property>
    </bean>

    <bean id="person7" class="com.hello.Person">
        <property name="car" ref="car1"></property>
    </bean>
  1. 內部值
    <!--內部Bean-->
    <bean id="person8" class="com.hello.Person">
        <property name="car">
            <bean id="car2" class="com.hello.Car">
                <property name="carName" value="奔馳"></property>
                <property name="color" value="Black"></property>
                <property name="price" value="300000.0"></property>
            </bean>
        </property>
    </bean>
  1. 對List或者數組賦值
    <bean id="book2" class="com.hello.Book">
        <property name="bookName" value="C++ book"></property>
        <property name="price" value="200"></property>
        <property name="author" value="Jack"></property>
    </bean>

    <!--對List<Book> 賦值 Book又爲一個類-->
    <bean id="person9" class="com.hello.Person">
        <property name="books">
            <list>
                <bean id="book1" class="com.hello.Book"
                      p:bookName="Java book" p:author="Tom" p:price="120"></bean>
                <ref bean="book2"></ref>
            </list>
        </property>
    </bean>
  1. 對Map賦值
    <bean id="person10" class="com.hello.Person">
        <property name="maps">
            <map>
                <entry key="key1" value="value1"></entry>
                <entry key="key2" value="value2"></entry>
                <entry key="key3" value="value3"></entry>
            </map>
        </property>
    </bean>
  1. 對Properties賦值
    <bean id="person11" class="com.hello.Person">
        <property name="properties">
            <props>
                <prop key="user01">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
  1. 利用util名稱空間配置集合類型
<util:map id="myMap">
    <entry key="key1" value="value1"></entry>
    <entry key="key2" value="value2"></entry>
</util:map>

<bean id="person12" class="com.hello.Person">
    <property name="maps" ref="myMap"></property>
</bean>

三. 工廠方法配置Bean

  1. 靜態工廠
import com.hello.Book;

public class BookStaticFactory {
    public static Book getInstance(String bookName){
        System.out.println("靜態工廠創建");
        Book book = new Book();
        book.setBookName(bookName);
        book.setAuthor("zhangsan");
        book.setBookId(123);
        return book;
    }
}
<!-- 靜態工廠方法   -->
<bean id="StaticFactory" class="com.factory.BookStaticFactory" factory-method="getInstance">
    <constructor-arg value="bookName1"></constructor-arg>
</bean>
  1. 實例工廠
import com.hello.Book;

public class BookInstanceFactory {
    public Book getInstance(String bookName){
        System.out.println("實例工廠創建");
        Book book = new Book();
        book.setBookName(bookName);
        book.setAuthor("zhangsan");
        book.setBookId(123);
        return book;
    }
}
<!-- 實例工廠方法   -->
<bean id="InstanceFactory" class="com.factory.BookInstanceFactory"></bean>

<bean id="book" class="com.hello.Book" factory-bean="InstanceFactory" factory-method="getInstance">
    <constructor-arg value="bookName2"></constructor-arg>
</bean>
  1. FactoryBean
import com.hello.Book;
import org.springframework.beans.factory.FactoryBean;
/**
 * Spring中的FactoryBean
 * */
public class BookFactoryBean implements FactoryBean<Book> {

    @Override
    public Book getObject() throws Exception {
        System.out.println("FactoryBean 創建");
        Book book = new Book();
        book.setBookName("BookName123");
        return book;
    }

    @Override
    public Class<?> getObjectType() {
        return Book.class;
    }

    @Override
    public boolean isSingleton() {
        return false;  // false爲多實例, true爲單實例
    }
}
<!-- FactoryBean工廠方法   -->
<bean id="FactoryBean1" class="com.factory.BookFactoryBean"></bean>

FactoryBean 對象創建時在getBean時創建,不管單實例還是多實例

四. Bean的高級配置

1. Bean的繼承

這裏的繼承只是說繼承Bean的配置,被繼承的bean稱爲父bean。繼承這個父bean的bean稱爲子bean,子bean從父bean中繼承配置,包括bean的屬性配置,子bean也可以覆蓋從父bean繼承過來的配置。
例如:

<bean id="dept" class="com.atguigu.parent.bean.Department">
	<property name="deptId" value="100"/>
	<property name="deptName" value="IT"/>
</bean>

<bean id="emp01" class="com.atguigu.parent.bean.Employee">
	<property name="empId" value="1001"/>
	<property name="empName" value="Tom"/>
	<property name="age" value="20"/>

	<!-- 重複的屬性值 -->	
	<property name="detp" ref="dept"/>
</bean>

<bean id="emp02" class="com.atguigu.parent.bean.Employee">
	<property name="empId" value="1002"/>
	<property name="empName" value="Jerry"/>
	<property name="age" value="25"/>

	<!-- 重複的屬性值 -->
	<property name="detp" ref="dept"/>
</bean>

避免重複可以讓 emp02 繼承 emp01:

<!-- 以emp01作爲父bean,繼承後可以省略公共屬性值的配置 -->
<bean id="emp02" parent="emp01">
	<property name="empId" value="1002"/>
	<property name="empName" value="Jerry"/>
	<property name="age" value="25"/>
</bean>

注意:
父bean可以作爲配置模板,也可以作爲bean實例。若只想把父bean作爲模板,可以設置的abstract屬性爲true,這樣Spring將不會實例化這個bean。
並不是元素裏的所有屬性都會被繼承。比如:autowire,abstract等。也可以忽略父bean的class屬性,讓子bean指定自己的類,而共享相同的屬性配置。但此時abstract必須設爲true。

2. Bean的依賴創建

有的時候創建一個bean的時候需要保證另外一個bean也被創建,這時我們稱前面的bean對後面的bean有依賴。例如:要求創建Employee對象的時候必須創建Department。這裏需要注意的是依賴關係不等於引用關係,Employee即使依賴Department也可以不引用它。

<bean id="emp03" class="com.atguigu.parent.bean.Employee" depends-on="dept">
	<property name="empId" value="1003"/>
	<property name="empName" value="Kate"/>
	<property name="age" value="21"/>
</bean>
3. Bean的作用域

作用域決定Bean是否是單實例還是多實例。
作用域有:

prototype: 多實例,容器啓動並不會創建對象,獲取的時候創建
singleton: 單實例 默認的,容器啓動就已經創建好對象
request: 和Web請求相關
session: 和會話相關

可以在元素的scope屬性裏設置bean的作用域,以決定這個bean是單實例的還是多實例的。

4. Bean的生命週期

Spring IOC容器可以管理bean的生命週期,Spring允許在bean生命週期內特定的時間點執行指定的任務。Spring IOC容器對bean的生命週期進行管理的過程:

[1] 通過構造器或工廠方法創建bean實例
[2] 爲bean的屬性設置值和對其他bean的引用
[3] 調用bean的初始化方法
[4] bean可以使用了
[5] 當容器關閉時,調用bean的銷燬方法

在配置bean時,通過 init-methoddestroy-method 屬性爲bean指定初始化和銷燬方法。
bean後置處理器允許在調用初始化方法前後對bean進行額外的處理。bean後置處理器對IOC容器裏的所有bean實例逐一處理,而非單一實例。其典型應用是:檢查bean屬性的正確性或根據特定的標準更改bean的屬性。bean後置處理器時需要實現接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被調用前後,Spring將把每個bean實例分別傳遞給上述接口的以下兩個方法:

postProcessBeforeInitialization(Object, String)
postProcessAfterInitialization(Object, String)

添加bean後置處理器後bean的生命週期過程:

[1] 通過構造器或工廠方法創建bean實例
[2] 爲bean的屬性設置值和對其他bean的引用
[3] 將bean實例傳遞給bean後置處理器的postProcessBeforeInitialization()方法
[4] 調用bean的初始化方法
[5] 將bean實例傳遞給bean後置處理器的postProcessAfterInitialization()方法
[6] bean可以使用了
[7] 當容器關閉時調用bean的銷燬方法

後置處理器實現類:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class BeanProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化前處理工作");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化後處理工作");
        return bean;
    }
}

生命週期控制的XML文件配置:

<bean id="car1" class="com.hello.Car" init-method="inintMethod" destroy-method="destroyMethod">
        <property name="price" value="12345"></property>
        <property name="color" value="red"></property>
    </bean>

    <bean id="beanPostProcesser" class="com.hello.BeanProcessor">

    </bean>
5. 引用外部屬性文件

當bean的配置信息逐漸增多時,查找和修改一些bean的配置信息就變得愈加困難。這時可以將一部分信息提取到bean配置文件的外部,以properties格式的屬性文件保存起來,同時在bean的配置文件中引用properties屬性文件中的內容,從而實現一部分屬性值在發生變化時僅修改properties屬性文件即可。這種技術多用於連接數據庫的基本信息的配置。
Xml中可以直接配置C3P0數據庫連接池,如:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="root"></property>
    <property name="password" value="tyltyl"></property>
    <property name="jdbcUrl">
        <value>jdbc:mysql://localhost:3306/test?serverTimezone=GMT&amp;characterEncoding=utf8</value>
    </property>
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="minPoolSize" value="5"></property>
    <property name="initialPoolSize" value="5"></property>
</bean>

通過context命名空間加載屬性文件來配置連接池:

    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
    </bean>

其中jdbc.properties文件中寫入

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT
jdbc.user=root
jdbc.password=tyltyl
6. 自動裝配

手動裝配:以value或ref的方式明確指定屬性值都是手動裝配。
自動裝配:根據指定的裝配規則,不需要明確指定,Spring自動將匹配的屬性值注入bean中。
自動裝配通過Bean設置autowire選項,其中有四種模式:
- 不自動裝配(autowire=“default”)
- 根據名字(autowire=“byName”)
通過屬性名作爲id去容器中尋找,找到了自動給裝配,找不到裝配null
- 根據類型(autowire=“byType”)
通過屬性的類型作爲依據去容器中尋找,找到了自動給裝配,找不到裝配null,如果有多個相同的類型,報錯。
- 根據構造器(autowire=“constructor”)
按照構造器進行賦值,其中規則是先按照構造器類型,沒有就裝配null,如果按類型找到多個,那麼按照參數名找,找不到就裝配null

五. SpEl(Spring表達式語言)

SpEl支持運行時查詢並可以操作對象圖,和JSP頁面上的EL表達式、Struts2中用到的OGNL表達式類似。SpEl使用 #{…} 作爲定界符,所有在大框號中的字符都將被認爲是SpEL表達式

  1. 字面量
<property name="age" value="#{23}"></property>         <!--整數-->
<property name="score" value="#{98.4}"></property>     <!--浮點數-->
<property name="sex" value="#{''}"></property>       <!--字符串-->
<property name="capcity" value="#{1e4}"></property>    <!--科學記數法-->
<property name="enabled" value="#{true}"></property>   <!--Boolean類型-->

2.引用其他bean或者引用其他bean的屬性值

<bean id="department1" class="com.hello.Department">
    <property name="name" value="IT"></property>
</bean>

<bean id="employee" class="com.hello.Employee">
    <property name="department" value="#{department1}"></property>
    <property name="departName" value="#{department1.name}"></property>
</bean>
  1. 調用非靜態方法
<bean id="book" class="com.hello.Book">
    <property name="bookName" value="XXXX"></property>
</bean>

<bean id="person" class="com.hello.Person">
    <property name="lastName" value="#{book.getBookName()}"
</bean>
  1. 調用靜態方法
    靜態方法使用 #{T(全類名).靜態方法名()} 模式
<bean id="employee" class="com.hello.Employee">
	<property name="circle" value="#{T(java.lang.Math).PI*2}"/>
</bean>

5.運算符
Spring表達式語言還包含各種常見的運算符

算術運算符:+、-、*、/、%、^
字符串連接:+
比較運算符:<、>、==、<=、>=、lt、gt、eq、le、ge
邏輯運算符:and, or, not, |
三目運算符:判斷條件?判斷結果爲true時的取值:判斷結果爲false時的取值
正則表達式:matches

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章