Spring-framework複習筆記一(IOC,bean以及DI)

1.耦合概念以及解耦

耦合: 簡單來說就是指程序間的依賴關係。一般包括:類之間的關係,方法間的關係。
解耦: 目的是爲了降低程序間的依賴關係。實際開發中應該做到:編譯時不依賴,運行時才依賴。

1.1.兩個例子:JDBC連接以及持久層業務層調用關係

創建一個demo類,在不導入mysql-connector-java.jar的情況下,執行程序。
(在導入jar包後,都可正常運行)
在這裏插入圖片描述
嘗試通過Class.forName(“驅動類全類名”)(反射機制)獲取驅動類:此時即使驅動類不存在,也不會在編譯的時候報錯
在這裏插入圖片描述
持久層和業務層的調用關係:此時若不存在持久層類,將無法通過編譯,這就是編譯期依賴
在這裏插入圖片描述

1.2.解耦的思路:通過工廠模式解耦(手動實現)

用工廠模式,通過反射機制,創建對象
第一步: 創建一個配置文件來配置我們的service和dao。內容爲:唯一標識 = 全限定類名(key - value)
第二步: 通過讀取配置文件中配置的內容,通過反射創建對象。

配置文件:
在這裏插入圖片描述
手寫的BeanFactory工廠類(實現單例模式):
PS:單例模式與多例模式:
單例模式: 多次創建(new)同一對象時,爲相同的對象,擁有相同的地址。
多例模式: 多次創建(new)同一對象時,爲不同的對象,擁有的地址不同。
本例實現單例模式的思路: 在類中新建一個Map用於存儲<String,Object>,在第一次運行的時候,將配置文件中的多個對象一次性創建並且加到Map中,之後在調用getBean方法的時候通過傳入的類名從Map中取出一開始就創建好的對象即可。

public class BeanFactory {
    //定義一個Properties對象
    private static Properties props;

    //定義一個Map,用於存放我們要創建的對象。我們把它稱之爲容器
    private static Map<String, Object> beans;

    //使用靜態代碼塊爲Properties對象賦值
    static {
        try {
            //實例化對象
            props = new Properties();
            //獲取properties文件的流對象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");//讀取配置文件
            props.load(in);
            //實例化容器
            beans = new HashMap<String, Object>();
            //取出配置文件中所有的key 一次性創建完成
            Enumeration keys = props.keys();
            //遍歷枚舉
            while (keys.hasMoreElements()){
                //取出每個key
                String key = keys.nextElement().toString();
                //根據key獲取value
                String beanPath = props.getProperty(key);
                //反射創建對象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器中
                beans.put(key,value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失敗!");
        }
    }
    /**
    * 根據bean的名稱獲取對象
    * 且從Map中獲取對象,實現了單例模式
    * */
    public static Object getBean(String beanName) {
        return beans.get(beanName);//通過類名找到對應的對象地址
    }
}

業務層使用getBean()方法從工廠中獲取對應類名的對象。
在這裏插入圖片描述工廠模式優點:
1、一個調用者想創建一個對象,只要知道其名稱就可以了。
2、擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。
3、屏蔽產品的具體實現,調用者只關心產品的接口。
工廠模式缺點:
每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

2.使用SpringIOC解決耦合(將對象的創建交給spring)

使用spring的方法獲取類,不再需要自己寫工廠。

2.1.基本實現

1.創建一個maven工程。pom.xml導入spring相關jar包
在這裏插入圖片描述
2.創建xml配置文件
導入spring約束,配置唯一標識id與相應類的對應關係。使得可以根據id獲得對應的對象。
在這裏插入圖片描述
3.創建測試類:從上到下代表了持久層,業務層,表現層的調用關係。
包和類的結構如下:
在這裏插入圖片描述
持久層:

/**
 * 賬戶的持久層實現類
 **/
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存了賬戶");
    }
}
/**
 * 持久層接口
 **/
public interface IAccountDao {
    /*
    * 模擬保存賬戶
    * */
    void saveAccount();
}

業務層:

/**
 * 業務層實現類
 **/
public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
/**
 * 業務層的接口
 **/
public interface IAccountService {
    /**
     * 模擬保存賬戶
     */
    void saveAccount();
}

表現層:

/**
 * 表現層調用業務層,業務層調用持久層
 * 模擬一個表現層,用於調用業務層
 **/
public class Client {
    /**
     * 獲取spring的Ioc核心容器,並根據id獲取對象
     * **/
    public static void main(String[] args) {
        //1,獲取核心容器對象。通過容器創建對象.通過核心容器的getBean()方法獲取具體對象.
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根據id獲取bean對象
        IAccountService as = (IAccountService) ac.getBean("accountService");//根據唯一標識獲得對象
        System.out.println(as);//嘗試輸出對象,默認情況爲單例模式
    }
}

2.1.1.ApplicationContext的三個實現類

ClassPathXmlApplicationContext: 它可以加載類路徑下的配置文件,要求配置文件必須在類路徑下,不在的話,加載不了。
FileSystemXmlApplicationContest: 它可以加載磁盤任意路徑下的配置文件(必須有訪問權限)
AnnotationConfigApplicationContest: 讀取註解創建容器。

2.1.2.核心容器BeanFactory和ApplicationContext有什麼區別

ApplicationContext: 它在構建核心容器時,創建對象採取的策略是採用立即加載的方式。也就是說,只要一讀取完配置文件馬上就創建文件中配置的對象。(單例對象適用)

BeanFactory: 它在構建核心容器時,創建對象採取的策略是採用延遲加載的方式。也就是說,什麼時候根據id獲取對象了,什麼時候才真正的創建對象。(多例對象適用)

PS:沒有特殊要求的情況下,應該使用ApplicationContext完成。因爲BeanFactory能完成的事情,ApplicationContext都能完成,並且提供了更多接近現在開發的功能。

3.XML文件的配置

使用配置文件實現IOC,要將託管給spring的類寫進bean.xml配置文件中。

3.1.bean標籤

作用: 配置託管給spring的對象,默認情況下調用類的無參構造函數,若果沒有無參構造函數則不能創建成功。
屬性:
1.id: 指定對象在容器中的標識,將其作爲參數傳入getBean()方法可以獲取獲取對應對象.
2.class: 指定類的全類名,默認情況下調用無參構造函數。
3.scope: 指定對象的作用範圍,可選值如下:
3.1.singleton:單例模式。(默認)
3.2.prototype: 多例模式。
3.3.request: 將對象存入到web項目的request域中。
3.4.session: 將對象存入到web項目的session域中。
3.5.global session: 作用於集羣環境的會話範圍(全局會話範圍),當不是集羣環境時,他就是session
4.init-method: 指定類中的初始化方法名稱,在對象創建成功之後執行
5.destroy-method: 指定類中銷燬方法名稱,對prototype多例對象沒有作用,因爲多利對象的銷燬時機不受容器控制。

3.2.bean的作用範圍和生命週期

1.單例對象: scope="singleton"
作用範圍: 每個應用只有一個該對象的實例,它的作用範圍就是整個應用。
生命週期: 單例對象的創建與銷燬 和 容器的創建與銷燬時機一致。
-------------出生:當容器創建時,對象出生。
-------------活着:只要容器存在,對象一直活着。
-------------死亡:容器銷燬,對象消亡
2.多例對象: scope="prototype"
作用範圍: 每次訪問對象時,都會重新創建對象實例。
生命週期: 多例對象的創建與銷燬時機不受容器控制。
-------------出生:當我們使用對象時,spring框架爲我們創建。
-------------活着:對象只要是在使用過程中就一直活着。
-------------死亡:當對象長時間不用且沒有別的對象引用時,由java的垃圾回收器回收。

3.3.實例化bean的三種方式

1.構造器實例化: 用默認構造函數創建。
在spring的配合文件中使用bean標籤,配以id和class屬性之後,且沒有其他屬性和標籤時。
採用的就是默認構造函數的bean對象,此時如果類沒有默認構造函數,則對象無法創建。(類沒有被重載的情況)

 <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>//xml文件 
 				//id爲標識符,class爲要通過getBean()創建的類

2.靜態工廠實例化: 使用靜態工廠中的靜態方法創建對象(使用某個類中的靜態方法創建對象,並存入spring容器)(靜態方法可以直接用類獲取。)

//			標識符				指定類									直接指定類中的靜態方法
<bean id="accountService" class="factory.StaticFactory" factory-method="getAccountService"></bean>
						

PS:類的構造函數也是靜態方法,因此默認無參構造函數也可以看作一種靜態工廠方法
3.實例工廠實例化: 使用普通工廠中的方法創建對象(使用某個類中的非靜態方法創建對象,並存入spring容器)。

/**
 * 模擬一個實例工廠類(該類可能是存在於jar包中的,我們無法通過修改源碼的方式來提供默認構造函數)
 **/
public class InstanceFactory {
    public IAccountService getAccountService(){//不是靜態方法
        return new AccountServiceImpl();
    }
}

目的:先創建實例工廠instanceFactory,然後調用getAccountService()方法創建對象。

		//標識符						指定類
<bean id="instanceFactory" class="factory.InstanceFactory"></bean>
		//標識符							指定實例工廠的id					指定實例工廠中生產對象的方法
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

4.Spring的依賴注入(DI)

4.1.依賴注入(Dependency Injection)

在通過Spring創建對象的時候,因爲使用的時反射機制而不是使用new關鍵字,因此我們需要在配置文件指定創建出對象各字段的取值。
能注入的數據有三類:
第一種:基本數據類型和String。
第二種:其他bean類型(在配置文件中或者註解配置過的bean)
第三種:複雜/集合類型
注入的方式有三種:
第一種:使用構造函數提供。
第二種:使用set方法提供(最常用)
第三種:使用註解提供(筆記二整理)

4.2.使用構造函數進行注入

首先是一個類:目的是在類創建的時候,指定字段name,age和birthday的值

public class AccountServiceImpl{
    //如果是經常變化的數據,並不適用於注入的方式
    private String name;
    private Integer age;
    private Date birthday;
    public AccountServiceImpl(String name,Integer age,Date birthday){//重寫的構造函數,必須賦值纔可創建成功
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
}

使用構造器注入:
使用的標籤:constructor-arg
標籤出現的位置:bean標籤的內部
標籤中的屬性:
type:用於指定要注入數據的數據類型,該數據類型也是構造函數中某個或某些參數的類型
index:用於指定要注入的數據給構造函數中指定索引位置的參數賦值。索引的位置是從0開始
name:用於指定給構造函數中指定名稱的參數賦值 (常用!這裏用的也是name)
上面三個用來指定給構造函數中的哪個參數賦值
value:用於提供基本類型和String類型的數據
ref:用於指定其他的bean類型數據。它指的就是在spring的Ioc核心容器中出現過的bean對象
優勢:在獲取bean對象時,必須注入操作,否則對象無法創建成功。
弊端:改變了bean對象的實例化方式,使我們在創建對象時,如果用不到這些數據,也必須提供。

 <bean id="accountService" class="service.impl.AccountServiceImpl">
        //		name設置對應字段,下面同理  value爲設置的初始值   ref爲引用的是bean標籤(爲了調用Date類生成日期)
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
    <!--配置一個日期對象,可以使用ref引用關聯的bean對象-->
    <bean id="now" class="java.util.Date"></bean>

4.3.使用set方法注入

首先是一個類:要生成對應的set方法

public class AccountServiceImpl2{
    //如果是經常變化的數據,並不適用於注入的方式
    private String name;
    private Integer age;
    private Date birthday;
    public void setName(String name){this.name = name;}
    public void setAge(Integer age){this.age = age;}
    public void setBirthday(Date birthday){this.birthday = birthday;}
}

使用set方法注入:
使用的標籤:property
出現的位置:bean標籤的內部
標籤的屬性:
name:用於指定主時所調用的set方法名稱。
value:用於提供基本類型和String類型的數據。
ref:用於指定其他的bean類型數據。它指的就是在spring的Ioc核心容器中出現過的bean對象。
優勢:創建對象時沒有明確的限制,可以直接使用默認構造函數。
弊端:如果有某個成員必須有值,則獲取對象時,有可能set方法沒有執行。

<bean id="accountService2" class="service.impl.AccountServiceImpl2">
        <property name="age"  value="21"></property>//相當方便
        <property name="name" value="TEST"></property>
        <property name="birthday" ref="now"></property>
    </bean>
    <bean id="now" class="java.util.Date"></bean>

4.4.複雜類型的注入/集合類型的注入

準備一個演示類:

public class AccountServiceImpl3{
   private String[] myStrs;//數組類型
   private List<String> myList;//集合類型
   private Set<String> mySet;//set類型
   private Map<String,String> myMap;//hash表類型
   private Properties myPros;//也是類似hash表類型
   //各自生成set方法
   public void setMyStrs(String[] myStrs) { this.myStrs = myStrs; }
   public void setMyList(List<String> myList) { this.myList = myList; }
   public void setMySet(Set<String> mySet) { this.mySet = mySet; }
   public void setMyMap(Map<String, String> myMap) { this.myMap = myMap; }
   public void setMyPros(Properties myPros) { this.myPros = myPros; }
    }

各自的標籤以及方法:

<bean id="accountService3" class="service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>value1</value>
                <value>value2</value>
                <value>value3</value>
            </array>
        </property>
        <property name="myList">
            <list>
                <value>value1</value>
                <value>value2</value>
                <value>value3</value>
            </list>
        </property>
        <property name="mySet">
            <set>
                <value>value1</value>
                <value>value2</value>
                <value>value3</value>
            </set>
        </property>
        <property name="myMap">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
        
        <property name="myPros">
            <props>
                <prop key="testC"></prop>
                <prop key="testD"></prop>
            </props>
        </property>
    </bean>

注意:總體來說分爲兩大結構,List和Map,相同結構的標籤內容可以替換使用。
用於給List結構集合注入的標籤:list,array,set
用於給Map結構集合注入的標籤:map,props

4.5.使用註解進行注入

待續。

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