1. 程式人生 > >Spring的IOC以及DI

Spring的IOC以及DI

Spring引入

* spring 是什麼?

    Spring是分層的 Java SE/EE應用 full-stack 輕量級開源框架,以 IoC(Inverse Of Control:
    反轉控制)和 AOP(Aspect Oriented Programming:面向切面程式設計)為核心,提供了展現層 Spring
    MVC 和持久層 Spring JDBC 以及業務層事務管理等眾多的企業級應用技術,還能整合開源世界眾多
    著名的第三方框架和類庫,逐漸成為使用最多的Java EE 企業應用開源框架。

* 程式的耦合
    耦合:程式之間的依賴關係
        包括了類與類之間的依賴,方法之間的依賴

    解耦:降低程式之間的依賴關係,提升程式的靈活性與可擴充套件性
    實際開發中:應該做到編譯期不依賴,執行時才會依賴

    解耦的思路:
        第一步:使用反射來建立物件,從而避免使用new關鍵字
        第二步:通過讀取配置檔案來獲取要建立的物件全限定類名

* 三層架構程式碼中存在的問題    
    業務層呼叫持久層的時候的new dao的介面
    在表現層呼叫業務層的時候new service的介面

    解決辦法: 使用工廠模式解耦(一個建立bean物件的工廠)
        Bean
            1. Bean:在計算機英語中,有可重用元件的含義
            2. JavaBean:用java語言編寫的可重用元件
                JavaBean > 實體類
            3. POJO(Plain Ordinary Java Object)即普通Java類
            4. 可以認為POJO就一個不帶方法而單純承載資料實體Bean;EJB是一個包含了POJO的超集、
                包含POJO之外的,具有方法、實現、功能這一群元件。
        方法:
            1. 一個配置檔案配置service與dao的內容(xml/properties)
            2. 通過讀取配置檔案來獲取要建立的物件全限定類名,反射載入
            3.優化:使用單例

Spring的IOC(Inversion Of Control)

* IOC:控制反轉
    簡單理解:讓spring來控制物件


* 入門案例:
    1. 匯入依賴
    2. 建立配置檔案,匯入約束,並寫入相關的bean標籤(配置id與class)
    3. 從容器中獲取容器物件
        3.1 先獲取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("配置檔案")
        3.2 從容器中根據id或者name獲取bean物件
        User user = ac.getBean("user", User.class);
        System.out.println(user);//User{userName='null', age=null, birthday=null}


* ApplicationContext的三個常用的實現類
    ClassPathXmlApplicationContext:它可以載入類路徑下的配置文的件,要求配置檔案必須在類路徑下,不在就載入不了
    FileSystemXmlApplicationContext:它可以載入磁碟任意路徑下的配置檔案    (必須有訪問許可權)
    AnnotationConfigApplicationContext:它是用於讀取註解的配置

* 核心容器的兩個介面引發出的問題
    ApplicationContext:(單例物件試用),多使用此介面
        它在構建核心容器的時候,建立物件採取的是立即載入的方式,也就是說,只要一讀取完配置檔案就馬上
        就建立配置檔案中的配置的物件
    BeanFactory:(多例物件適用)
        它在構建核心容器的時候,採取的策略是延遲載入的方式,也就是說,什麼時候根據id獲取物件了,什麼時候才會
        真正的建立物件

* Spring對bean的管理細節
    1. 建立bean的三種方式:

        1.1 第一種方式:使用預設建構函式建立
            在spring的配置檔案中使用bean標籤,配置id與class屬性之後,且沒有其他屬性和標籤時
            採用的就是預設建構函式建立bean物件,測試如果類中沒有預設建構函式,則物件無法建立
            <bean id="service" class="com.qin.service.impl.IUserServiceImpl"></bean>
        1.2 第二種方式:使用普通工廠中的方法來建立物件(使用某個類中的方法建立物件,並存入spring容器)
            <bean id="instanceFactory" class="com.qin.factory.instanceFactory"></bean>(要先有物件才能呼叫方法)
            <bean id="serviceFactory" class="com.qin.factory.instanceFactory" factory-method="getAccount">
            </bean>
        1.3 第三種方式:使用工廠中的靜態方法建立物件(使用某個類中的靜態方法建立物件,並存入spring容器)
            <bean id="serviceFactory" class="com.qin.factory.StaticFactory" factory-method="getAccount">
            </bean>
            靜態方法是不需要物件的,是可以通過全類名呼叫的,所以不需要物件

    2. bean的作用範圍
            bean標籤的scope屬性
                作用:用於指定bean 的作用範圍
                取值:
                    singleton:單例的(預設值)
                    prototype:多例的
                    request:作用於wen應用的請求範圍
                    session:作用於wen應用的會話範圍
                    global-session:作用於叢集環境的會話範圍(全域性會話範圍),當不是叢集的時候,相當於session
    3. bean物件的生命週期
        單例物件:
            出生:當容器建立時候物件出生
            存在:只要容器在,物件就一直活著
            死亡:容器銷燬,物件消亡
            總結:單例物件的生命週期和容器相同
        多例物件:
            出生:當使用物件時,spring框架為我們建立
            存在:物件還在使用的過程中就一直存活
            死亡:當物件長時間不用,且沒有別的物件引用時,由java的垃圾回收器回收

Spring依賴注入:DI(依賴關係的維護)

    * 能注入的資料:三類
            1. 基本型別和string :
            2. 其他bean型別(在配置檔案中或者註解配置過的bean)
            3. 複雜型別/集合型別
    * 注入的方式:有三種
            1. 使用建構函式提供(不常用)
                使用的標籤:constructor-arg
                標籤出現的位置:bean標籤的內部
                標籤的屬性:
                    type:用於指定要注入的資料的資料型別,該資料型別也是建構函式中某個或某些引數的型別
                    index:用於指定要注入的資料給建構函式中指定索引位置的引數賦值,索引的位置是從0開始的
                    name:用於指定給建構函式中指定名稱的引數賦值(常用)
                    =============以上三個用於指定給建構函式中的哪個引數賦值==================
                    value:用於提供基本型別和string型別的資料
                    ref:用於指定其他的bean型別資料.他指的就是在spring的核心容器中出現過的bean物件

                    優勢:
                        在獲取bean物件時,注入資料是必須的操作,否則物件無法建立成功.
                    弊端:
                        改變了bean物件的例項化方式,使我們在建立物件時,如果不使用這些資料也必須提供.

            2. 使用set方法提供(常用)
                * 首先必須有set方法
                涉及的標籤:property
                出現的位置:bean標籤的內部
                標籤的屬性:
                    name:用於指定注入時呼叫的set方法名稱
                    value:用於提供基本型別和string型別的資料
                    ref:用於指定其他的bean型別資料.他指的就是在spring的核心容器中出現過的bean物件

                優勢:建立物件時沒有明確的限制,可以直接使用預設建構函式
                弊端:如果某個成員必須有值,則獲取物件是有可能set方法沒有執行

            3. 使用註解提供

    * 示例:
        1. 建構函式注入
            <bean id="userDao" class="com.qin.dao.impl.IUserDaoImpl">
                <constructor-arg name="userName" value="sakura"></constructor-arg>
                <constructor-arg name="age" value="18"></constructor-arg>
                <constructor-arg name="birthday" ref="date"></constructor-arg>
            </bean>

            <bean id="date" class="java.util.Date"></bean>

        2. 使用set方法注入
             <bean id="userDao1" class="com.qin.dao.impl.IUserDaoImpl1">
                <property name="age" value="19"></property>
                <property name="userName" value="tom"></property>
                <property name="birthday" ref="date"></property>
            </bean>

            <bean id="date" class="java.util.Date"></bean>

        3. 對集合屬性進行set方式注入
            <bean id="userDao2" class="com.qin.dao.impl.IUserDaoImpl2">
                <property name="myStrs">
                    <array>
                    <value>string</value>
                    <value>string</value>
                    <value>string</value>
                    </array>
                </property>

                <property name="myList">
                    <list>
                        <value>list</value>
                        <value>list</value>
                        <value>list</value>
                    </list>
                </property>
                <property name="mySet">
                    <set>
                        <value>set</value>
                        <value>set</value>
                        <value>set</value>
                    </set>
                </property>
                <property name="myMap">
                    <map>
                        <entry key="map1">
                            <value>map1</value>
                        </entry>
                        <entry key="map2" value="map2"></entry>
                        <entry key="map3" value="map3"></entry>
                    </map>
                </property>
                <property name="myProps">
                    <props>
                        <prop key="prop">123</prop>
                        <prop key="prop">123</prop>
                        <prop key="prop">123</prop>
                    </props>
                </property>
            </bean>

 

Spring的基於註解開發

* IOC在XML的配置:<bean id="accountService" class="com.qin.service.impl.AccountService" scope=""
        init-method="" destory-method="">
        <property name="" value="" |ref=""></property>
         </bean>

* 註解分類:
    1. 用於建立物件的
        它們的作用就和xml配置中編寫一個<bean>標籤實現的功能是一樣的

        @Component:
            作用:
                用於把當前類物件存入Spring容器中
            屬性:
                value:用於指定bean中的id.當我們不寫時,他的預設值是當前類名的,且首字母小寫

        @Controller:一般用在表現層
        @Service:一般用在業務層
        @Repository:一般用在持久層
        以上三個註解的作用和屬性與Component是一模一樣的
        他們三個是spring為我們提供明確的三層使用的註解,使我們的三層物件更加清晰

        在xml檔案的配置:
            <context:component-scan base-package="com.qin"></context:component-scan>
            作用:告知spring在建立容器時要掃描的包,配置所需要的標籤不是beans約束中,而是在context
            的名稱空間和約束中

            約束配置:
            <?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:context="http://www.springframework.org/schema/context"
                xsi: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">

    2. 用於注入資料的
        它們的作用就和在xml配置檔案中的bean標籤中寫一個<property>標籤的作用是一樣的

        @Autowired
            作用:(自動按照型別注入)    
                如果ioc容器中有唯一的一個bean物件型別和要注入的變數型別匹配,就可以注入成功
                如果ioc容器中沒有任何bean型別和要注入的變數型別一致,將會注入失敗
                如果ioc容器中有多個bean型別和要注入的變數型別一致,會先根據型別尋找,再根據變數名找
            出現位置:
                變數上,方法上(在set方法上也可以使用);
            細節:
                在使用註解注入時,set方法就不是必須的
        @Qualifier
            * 配合@Autowired使用
            * 也可以存在於引數上,配合指定構造中要傳遞的bean物件
            作用:
                在按照類中注入的基礎之上再按照名稱注入.他在給類成員注入時不能單獨使用,但是在給方法引數注入時可以
            屬性:
                value:用於指定注入bean的id
        @Resource
            * jdk1.9不能使用
            作用:
                直接按照bean的id注入,它可以單獨使用
            屬性:
                name:用於指定bean 的id

        * 以上三個注入都只能注入其他bean型別,而基本型別和string型別無法使用上述註解實現,集合型別也可以用Autowired實現

        @Value
            作用:
                用於注入基本型別和string型別的資料
            屬性:
                value:用於指定資料的值.他可以使用spring和spEL(也就是spring的el表示式)
                spEL的寫法:#{表示式}

                #{}:去容器中找id為花括號內容對應的值
                ${}:去配置檔案中讀取花括號對應的值
    3. 用於改變作用範圍的
        它們的作用就和在xml配置檔案中的bean標籤中使用scope屬性實現的功能是一樣的

        @Scope
            作用:
                用於指定bean的作用範圍
            屬性:
                value:指定範圍的取值.常用取值:singleton prototype

    4. 和生命週期相關
        它們的作用就和在xml配置檔案中的bean標籤中使用init-method和destory-method的作用是一樣的

        @PreDestroy
            作用:
                用於指定銷燬方法
        @PostConstruct
            作用: 
                用於指定初始化方法


* spring的新註解(可以使用純註解的形式開發)
    * 建立一個類.替換核心配置檔案
    @Configuration
        作用:
            指定當前類是一個配置類
        細節:
            當配置類作為AnnotationConfigApplicationContext物件建立的引數時,可以不寫此註解
            但不是任何時候都可以不寫(把註解檔案分開寫,另一個沒有讀的配置檔案必須寫)
    @ConponentScan
        作用:
            用於通過註解指定spring在建立容器時要掃面的包
        屬性:
            value:它和basePackage的作用是一樣的,都是用於指定建立容器時要掃面的包
            我們使用次註解就等同於在xml中配置了:
            <context:component-scan base-package="com.qin"></context:component-scan>


    @Bean
        作用:
            用於把當前方法的的返回值作為bean物件存入spring的ioc容器中
        屬性:
            name:用於指定bean的id,當不寫時,預設值是當前方法的名稱
        細節:
            當我們使用註解來配置方法時,如果方法有引數,spring框架會去容器中查詢有沒有可用的bean物件,查詢的方式和
            @Autowired註解的作用一樣的

    @Import
        作用:
            用於匯入其他的配置類
        屬性:
            value:用於指定其他配置類的位元組碼檔案
            當我們使用import的註解之後,有import註解的類就是父配置類,而匯入的都是子配置類

    @PropertySource
        作用:
            用於指定properties檔案的位置
        屬性:
            value:指定檔案的名稱以及檔案的路徑
                關鍵字:classpath,表示類路徑下
        例子:
            @PropertySource("classpath:JdbcConfig.properties")


    *沒有xml檔案時測試就要修改ApplicationContext的建立方式
    ApplicationContext ac = new AnnotationConfigApplicationContext("被@Configuration註解過的類".class)

抽取配置資料庫檔案

1.抽取檔案:
jdbcConfig.properties
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring
    jdbc.username=root
    jdbc.password=root

2. 在Spring的配置檔案中配置:
    <context:property-placeholder location="jdbcConfig.properties"></context:property-placeholder>
    使用註解的配置為:
        @PropertySource("classpath:jdbcConfig.properties")


3. 在原來的配置檔案中使用Spring的el表示式
    ${}來表示
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password"></property>