1. 程式人生 > >Spring學習--IOC依賴注入

Spring學習--IOC依賴注入

最近在玩SpringBoot,因為要做分享,所以拿出Spring去做個對比,才能感受到使用SpringBoot的簡單和快速性。

一.Spring定義

Spring是一個開源框架,為了解決企業應用開發的複雜性而建立的,但現在已經不止應用與企業應用。

Spring是一個輕量級的控制反轉(IOC)和麵向切面程式設計(AOP)的容器框架。

--從大小與開銷方面看來,Spring都是輕量級的;

--通過控制反轉IOC的技術達到鬆耦合的目的(把控制權交出去,在使用的時候直接用);

--提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發;

--包含並管理應用物件的配置和生命週期,這個意義上是一種容器;

--將簡單的元件配置組合成複雜的應用,這個意義上是框架。

二.Spring作用

  • 容器;
  • 提供了多種技術的支援(JMS,MQ支援,UnitTest等);
  • AOP(事務管理,日誌管理等);
  • 提供了眾多方便應用的輔助類(JDBC Template等);
  • 對主流應用框架提供了良好的支援(Hibernate等)。

三.適用範圍

  • 構建企業級應用;
  • 單獨使用Bean容器(Bean管理);
  • 單獨使用AOP進行切面管理;
  • 其他的Spring功能,如對訊息的支援等;
  • 在網際網路中的應用。

四.IOC控制反轉

控制權的轉移,應用程式本身不負責依賴物件的建立和維護,而是由外部容器來負責建立和維護,DI(依賴注入)是其一種實現方式。

目的:建立物件並組裝物件間的關係。

依賴注入:由IOC容器在執行中,動態的將某種依賴關係注入物件中。

五.Spring的常用注入方式

Spring注入是指在啟動spring容器載入bean配置時,完成對變數的賦值注入。

(1)設值注入(set注入):

         配置檔案如下:

           property 表示InjectionServiceImpl有一個屬性(成員變數)injectDAO

           ref表示引用id

           通過set注入,自動呼叫set方法,InjectServiceImpl中一定有一個setInjectDAO方法。

<?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">

    <bean id="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
        <property name="injectionDAO" ref="injectionDAO"></property>
    </bean>

    <bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>

</beans>

使用程式碼如下:

package com.spring.beananocation;
/**
 * DAO介面
 */
public interface InjectionDAO {
    String save(String string);
}
package com.spring.beananocation;

/**
 * DAO實現類
 */
public class InjectionDAOImpl implements InjectionDAO {
    @Override
    public String save(String string) {
        return "這是一個儲存:" + string;
    }
}
package com.spring.beananocation;
/**
 * service 介面
 */
public interface InjectionService {
    void save(String arg);
}
package com.spring.beananocation;

/**
 * service 實現類
 */
public class InjectionServiceImpl implements InjectionService {

    /**
     * 和配置檔案中property同名的成員變數
     */
    private InjectionDAO injectionDAO;

    /**
     * set方法
     */
    public void setInjectionDAO(InjectionDAO injectionDAO) {
        this.injectionDAO = injectionDAO;
    }

    @Override
    public void save(String arg) {
        //模擬業務操作
        System.out.println("service接收引數:" + arg);
        arg = arg + " :" + this.hashCode();
        System.out.println(injectionDAO.save(arg));

    }
}

單元測試方法:

    @Test
    public void testSetter() {
        InjectionServiceImpl injectionService = super.getBean("injectionService");
        injectionService.save("這是要儲存的資料");
    }

可以看到單元測試執行成功,injectionDAO被成功注入,執行結果如下:

(2)構造注入: 

使用構造方法建立類的例項化時,把相應的injectionDAO注入。

配置檔案如下:

<?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">

    <bean id="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
        <constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
    </bean>

    <bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>

</beans>

將InjectServiceImpl的set方法變為構造方法來注入:

package com.spring.beananocation;

/**
 * service 實現類
 */
public class InjectionServiceImpl implements InjectionService {

    /**
     * 和配置檔案中property同名的成員變數
     */
    private InjectionDAO injectionDAO;

    /**
     * 構造方法
     */
    public InjectionServiceImpl(InjectionDAO injectionDAO) {
        this.injectionDAO = injectionDAO;
    }

    @Override
    public void save(String arg) {
        //模擬業務操作
        System.out.println("service接收引數:" + arg);
        arg = arg + " :" + this.hashCode();
        System.out.println(injectionDAO.save(arg));

    }
}

單元測試方法和結果同設定注入。

建議以設值注入為主,構造注入為輔的方式來進行注入。

對於依賴關係無需變化的注入,儘量採用構造注入;而其他依賴關係的注入,則優先考慮設值注入。

五.Bean的配置項

  • id:在整個ioc容器中,這個Bean的唯一標識;
  • class:具體要例項化的哪一個類;
  • scope:指範圍,作用域;
  • Constructor arguments:構造器的引數(構造注入用)。
  • Properties:屬性(設值注入用)。
  • AutoWiring mode:自動裝配模式。
  • lazy-initialization mode:懶載入模式。
  • Initialization/destruction method:初始化和銷燬方法。

六.Bean的作用域

  • singleton :單例模式,預設選項,指一個Bean容器中只存在一份。(啟動時就建立。)
  • prototype:每次請求(每次使用)建立新的例項,destory方法不生效,因為請求完成後,例項就被垃圾回收器回收了,不存在了。(在用到物件時才建立。)
  • request:每次http請求建立一個例項且僅在當前request內有效。
  • session:同request,每次http請求建立,當前session內有效。
  • global session:基於portlet的web中有效(portel定義了global session),如果是在web中,同session。(有時候做系統和應用的整合,單點登入等。)

在配置檔案中加scope配置項:

<?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">

    <bean id="injectionService" class="com.spring.beananocation.InjectionServiceImpl" scope="singleton">
        <constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
    </bean>

    <bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>

</beans>

修改單元測試用例如下,由於我們打出了hashcode:

    /**
     * 單元測試
     */
    @Test
    public void testSetter() {
        InjectionServiceImpl injectionService = super.getBean("injectionService");
        injectionService.save("這是要儲存的資料");
        InjectionServiceImpl injectionService2 = super.getBean("injectionService");
        injectionService2.save("這是要儲存的資料");
    }

所以  當 scope="singleton"或者不加scope配置項時,為單例模式。

所以兩次輸出的hashcode為相同的,如下:

而當scope="prototype",每次使用都是一個新的例項。輸出的hashcode不一致,如下:

 

七.Bean 的生命週期 

  • 定義:在Spring的bean的xml檔案中配置的bean,定義了一個id和class;
  • 初始化:當ioc容器在啟動的時候去載入bean配置檔案裡面的bean並初始化,生成bean的例項。
  • 使用:從bean容器中取出一個bean的例項,去呼叫它的方法。
  • 銷燬:在bean容器停止的時候,去銷燬由當前這個bean建立的所有例項。

Bean初始化和銷燬的三種方式:

(1)配置init-method和destroy-method方法。

<?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">


    <bean id="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle" init-method="init"
          destroy-method="beanDestroy"></bean>

</beans>

BeanLifeCycle類:

package com.spring.ioc.impl;

public class BeanLifeCycle {
    public void init() {
        System.out.println("bean start");
    }

    public void beanDestroy() {
        System.out.println("bean stop");
    }


    public void hello() {
        System.out.println("hello~");
    }

}

單元測試方法:

    @Test
    public void beanLifeCycle(){
        BeanLifeCycle beanLifeCycle = super.getBean("beanLifeCycle");
        beanLifeCycle.hello();
    }

單元測試執行結果如下:

可以看到是先初始化,再使用,再銷燬。

(2)實現InitializingBean和DisposableBean介面,並重寫他們的afterPropertiesSet()和destroy()方法。不用修改配置檔案。

package com.spring.ioc.impl;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class BeanLifeCycle implements InitializingBean, DisposableBean {


    public void hello() {
        System.out.println("hello~");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("bean DisposableBean");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("bean InitializingBean");
    }
}

(3)在配置檔案配置全域性預設初始化銷燬方法:

配置default-init-method和default-destroy-method屬性。

<?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"
       default-init-method="defaultInit" default-destroy-method="defaultDestory">

    <bean id="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle"></bean>

</beans>

BeanLifeCycle類:

package com.spring.ioc.impl;

public class BeanLifeCycle{

    public void defaultInit() {
        System.out.println("defaultInit start");
    }

    public void defaultDestory() {
        System.out.println("defaultDestroy stop");
    }

    public void hello() {
        System.out.println("hello~");
    }
}

當3種方式都實現的時候,先執行實現介面的方法,再執行單獨配置的init,destory方法,全域性預設的方法不實現。

(只要實現了單獨配置的init和destory方法,就不再實現全域性預設方法了。)

八.Bean的自動裝配(Autowiring)

  • NO:不做任何配置(預設選項);
  • byName:根據屬性名自動裝配。此選項將檢查容器並根據名字查詢與屬性完全一致的bean,並將其與屬性自動裝配。bean id重複,會啟動失敗,提示同一個XML下不能存在相同的bean。
  • byType:如果容器種存在一個與指定屬性型別相同的bean,那麼將與該屬性自動裝配,如果存在多個該型別的bean,那麼丟擲異常,並指出不能使用byType方式進行自動裝配,如果沒找到相匹配的bean,則什麼事都不發生。
  • Constructor:與byType方式類似,不同之處在於它應用於構造器引數。如果容器種沒有找到與構造器引數型別一致的bean,那麼丟擲異常。

在配置檔案中加入配置default-autowire="byName"或者default-autowire="byType",都是通過set方法來賦值的。

<?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"
       default-autowire="byName">

    <bean id="autoWiringService" class="com.spring.ioc.autowiring.AutoWiringService"></bean>

    <bean id="autoWiringDAO" class="com.spring.ioc.autowiring.AutoWiringDAO"></bean>
</beans>
AutoWiringService:
package com.spring.ioc.autowiring;

public class AutoWiringService {
    private AutoWiringDAO autoWiringDAO;


    public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
        System.out.println("走set方法");
        this.autoWiringDAO = autoWiringDAO;
    }

    public void say(String words) {
        this.autoWiringDAO.say(words);
    }
}
AutoWiringDAO:
package com.spring.ioc.autowiring;

public class AutoWiringDAO {
    public void say(String word) {
        System.out.println("AutoWiringDAO:" + word);
    }
}

單元測試:

    @Test
    public void autoWiringServiceTest() {
        AutoWiringService autoWiringService = super.getBean("autoWiringService");
        autoWiringService.say("This is a test!");
    }

 執行結果:

在配置檔案中加入配置default-autowire="constructor"或者default-autowire="byType",是通過構造方法來賦值的。

AutoWiringService:

package com.spring.ioc.autowiring;

public class AutoWiringService {
    private AutoWiringDAO autoWiringDAO;

    public AutoWiringService(AutoWiringDAO autoWiringDAO) {
        System.out.println("走構造方法");
        this.autoWiringDAO = autoWiringDAO;
    }

    public void say(String words) {
        this.autoWiringDAO.say(words);
    }
}

九.Bean裝配之Resources

Resources為針對資原始檔的統一介面

  • UrlResource:URL對應的資源,根據一個URL地址即可構建。
  • ClassPathResource:獲取類路徑下的資原始檔。
  • FileSystemResource:獲取檔案系統裡面的資源。
  • ServletContextResource:ServletContext封裝的資源,用於訪問ServletContext環境下的資源。
  • InputStreamResource:針對於輸入流封裝的資源。
  • ByteArrayResource:針對於位元組組封裝的資源。