1. 程式人生 > 其它 >3、Spring的DI依賴注入

3、Spring的DI依賴注入

一、DI介紹

1、DI介紹

  • 依賴注入,應用程式執行依賴的資源由Spring為其提供,資源進入應用程式的方式稱為注入。
  • Spring容器管理容器中Bean之間的依賴關係,Spring使用一種被稱為“依賴注入”的方式來管理Bean之間的依賴關係。
  • 使用依賴注入,不僅可以為Bean注入普通的屬性值,還可以注入其他Bean的引用。依賴注入是一種優秀的解耦方式,其可以讓Bean以配置檔案組織在一起,而不是以硬編碼的方式耦合在一起。

2、依賴注入

依賴注入: Dependency Injection。它是 spring 框架核心 ioc 的具體實現。

3、為什要依賴注入

直接用物件,不需要去new物件。所謂的注入就是建立物件的過程而已。

  • 傳統的程式碼,每個物件負責管理與自己需要依賴的物件,導致如果需要切換依賴物件的實現類時,需要修改多處地方。同時,過度耦合也使得物件難以進行單元測試。

  • 依賴注入把物件的創造交給外部去管理,很好的解決了程式碼緊耦合(tight couple)的問題,是一種讓程式碼實現鬆耦合(loose couple)的機制。

  • 例如:我們控制層呼叫業務層需要在控制層建立業務層的物件,之前我們需要Person p = new Person;現在不需要我們自己去new person,直接交給spring去建立,我可以直接拿過來用。

Spring 支援的注入方式共有四種: set 注入、構造器注入、靜態工廠注入、例項化工廠注入。

4、IOC與DI之間的關係

IoC(Inversion of Control 控制反轉):是一種面向物件程式設計中的一種設計原則,用來減低計算機程式碼之間的耦合度。其基本思想是:藉助於“第三方”實現具有依賴關係的物件之間的解耦。

DI(Dependence Injection 依賴注入):將例項變數傳入到一個物件中去(Dependency injection means giving an object its instance variables)。

  • 控制反轉是一種思想
  • 依賴注入是一種設計模式
  • IoC框架使用依賴注入作為實現控制反轉的方式

說人話:就是DI與IOC相輔相成,相互包含。不能說成DI就是IOC,這兩個本質區別是:IOC是思想,DI設計模式。

二、Spring的依賴注入方式

1、Set注入

1、set注入介紹

思考:呼叫某個類的方法,你是怎麼操作的?

答:我通過該類的構造器建立物件,通過物件去呼叫方法。

看圖說話:

方式一與方式二對比:

  • 方式二沒有主動的去例項物件,而是通過帶引數的方法傳遞過來UserDao物件,從而實現UserService對UserDao的依賴.

  實際上建立物件是交給Spring容器來建立的

  • 屬性欄位需要提供set方法。並且還需要通過修改spring的配置檔案,進行相關的注入資訊配置。

2、Set注入標籤介紹

名稱:property

型別:標籤

歸屬:bean標籤

作用:使用set方法的形式為bean提供資源

格式:
    <bean>
     <property />
    </bean>
基本屬性:

 <property name="propertyName" value="propertyValue" ref="beanId"/>
    name:對應bean中的屬性名,要求該屬性必須提供可訪問的set方法(嚴格規範為此名稱是set方法對應名稱)

    value:設定非引用型別屬性對應的值,不能與ref同時使用

    ref:設定引用型別屬性對應bean的id ,不能與value同時使用

    注意:一個bean可以有多個property標籤,屬性欄位需要提供set方法

3、測試Set注入方式建立物件

  1. 在UserServiceImpl建立UserDao的變數,並在UserServiceImpl變數提供Set方法。
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao){

        this.userDao = userDao;

    }

    @Override
    public void saveUser() {
        System.out.println("service層通過注入的方式呼叫dao層,成功啦!");
        userDao.saveUser();
    }

  1. 介面類UserService
public interface UserService {
    void saveUser();
}
  1. dao層方法
public class UserDao {

    public void saveUser(){
        System.out.println("我是dao層");
    }
}

4.配置檔案配置

<?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="userService" class="com.why.service.impl.UserServiceImpl">
                <!--
                    <property/> 中的name對應set方法的set後面的名字且首字母小寫
                     ref 代表注入的是引用型別,所以需要填入藥注入bean的id
                    說明:service引用到層,相當於在service裡面建立userDao的物件,直接被service引用,與new物件呼叫方法類似
                -->
                <property name="userDao" ref="userDao"/>
        </bean>

        <!--將注入的資源宣告為bean,交由spring管理-->
        <bean id="userDao" class="com.why.dao.UserDao"/>
</beans>
  1. 測試類
public class UserController {
    public static void main(String[] args) {

        //獲取Spring上下文環境 (載入配置檔案)
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        /*
              通過getBean方法得到Spring容器中例項化好的Bean物件 (例項化Bean物件)
              userService代表的是配置檔案中bean標籤的id屬性值(id標識唯一的bean)
         */
        UserService userService = (UserService) context.getBean("userService");
        userService.saveUser();
    }
}

6.測試結果

程式沒有報錯,並正確輸出我們要列印的東西

2、構造方法注入

1、構造器注入標籤介紹

名稱:constructor-arg

型別:標籤

歸屬:bean標籤

作用:使用構造方法的形式為bean提供資源,相容早期遺留系統的升級工作

格式:
    <bean>
     <constructor-arg />
    </bean>
    
基本屬性:
  <constructor-arg name="argsName" value="argsValue />
        
        name:對應bean中的構造方法所攜帶的引數名
        value:設定非引用型別構造方法引數對應的值,不能與ref同時使用

其他屬性:

 <constructor-arg index="arg-index" type="arg-type" ref="beanId"/>
 
 ref:設定引用型別構造方法引數對應bean的id ,不能與value同時使用

 type :設定構造方法引數的型別,用於按型別匹配引數或進行型別校驗

 index :設定構造方法引數的位置,用於按位置匹配引數,引數index值從0開始計數

 注意:一個bean可以有多個constructor-arg標籤

2、構造注入測試案例

  1. 在UserServiceImpl建立UserDao的變數,並在UserServiceImpl變數提供構造方法。
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    private String name;
    private int age;
    
    //根據構造器進行注入
    public UserServiceImpl(UserDao userDao, String name, int age) {
        this.userDao = userDao;
        this.name = name;
        this.age = age;
    }
    
    
    @Override
    public void saveUser() {
        System.out.println("name="+name);
        System.out.println("age="+age);
        userDao.saveUser();
    }

    public void destroy(){
        System.out.println("bean銷燬");
    }
}
  1. 修改配置檔案
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   <!--
        構造器注入
    -->
    <bean id="userService" class="com.why.service.impl.UserServiceImpl">
        <!--
           對構造器進行賦值
       -->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        <constructor-arg name="age" value="22"></constructor-arg>
        <constructor-arg name="name" value="laity"/>

    </bean>

    <bean id="userDao" class="com.why.dao.UserDao"></bean>
</beans>

3. 其它程式碼與set注入程式碼相同

  1. 測試結果
  1. 構造標籤的屬性
//根據構造器進行注入
    public UserServiceImpl(UserDao userDao, String name, int age) {
        this.userDao = userDao;
        this.name = name;
        this.age = age;
    }

帶name屬性

<bean id="userService" class="com.why.service.impl.UserServiceImpl">
        <!--
           對構造器進行賦值
       -->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        <constructor-arg name="age" value="22"></constructor-arg>
        <constructor-arg name="name" value="laity"/>

</bean>

不帶name屬性,順序要按照構造方法的引數型別進行設定

<bean id="userService" class="com.why.service.impl.UserServiceImpl">
        <!--
           對構造器進行賦值
       -->
        <constructor-arg  ref="userDao"></constructor-arg>
        <constructor-arg  value="22"></constructor-arg>
        <constructor-arg  value="laity"/>

</bean>

index0表示第一個引數,index1表示第二個引數,以此類推

<bean id="userService" class="com.why.service.impl.UserServiceImpl">
        <!--
           對構造器進行賦值
       -->
        <constructor-arg index ="0" ref="userDao"></constructor-arg>
        <constructor-arg index ="1" value="22"></constructor-arg>
        <constructor-arg  index ="2" value="laity"/>
</bean>

3、集合注入

1、集合型別標籤介紹

名稱:array,list,set,map,props

型別:標籤

歸屬:property標籤 或 constructor-arg標籤

作用:注入集合資料型別屬性

格式:
    <property>
     <list></list>
    </property>

1、集合型別資料注入——list(重點)

<property name="xxx">
   <list>
       <value>why</value>
       <value>66666</value>
   </list>
</property>

(2)集合型別資料注入——props(重點)

<property name="xxx">
   <props>
       <prop key="name">javaxxf</prop>
       <prop key="value">666666</prop>
   </props>
</property>

(3)集合型別資料注入——array (瞭解)

<property name="xxx">
   <array>
       <value>123456</value>
       <value>66666</value>
   </array>
</property>

(4)集合型別資料注入——set(瞭解)

<property name="xxx">
    <set>
        <value>javaxxf</value>
        <value>66666</value>
    </set>
</property>

(5)集合型別資料注入——map(瞭解)

<property name="xxx">
   <map>
       <entry key="name" value="javaxf66666"/>
       <entry key="value" value="66666"/>
   </map>
</property><property name="xxx">
   <map>
       <entry key="name" value="javaxf66666"/>
       <entry key="value" value="66666"/>
   </map>
</property>

2、測試集合注入

  1. 建立集合並提供set方法
package com.why.service.impl;

import com.why.service.UserService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;

public class UserServiceImpl implements UserService {

    private ArrayList arrayList;
    private Properties properties;
    private int[] arr;
    private HashSet hset;
    private HashMap hmap;

    public void setArrayList(ArrayList arrayList) {
        this.arrayList = arrayList;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setArr(int[] arr) {
        this.arr = arr;
    }

    public void setHset(HashSet hset) {
        this.hset = hset;
    }

    public void setHmap(HashMap hmap) {
        this.hmap = hmap;
    }

    @Override
    public void saveUser() {
        System.out.println("ArrayList"+arrayList);
        System.out.println("Properties"+properties);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
        System.out.println("HashSet"+hset);
        System.out.println("HashMap"+hmap);
    }
}
  1. 配置檔案
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="userService" class="com.why.service.impl.UserServiceImpl">
        <!--list集合配置-->
        <property name="arrayList">
            <list>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </list>
        </property>
        <!--properties配置-->
        <property name="properties">
            <props>
                <prop key="properties1">java</prop>
                <prop key="properties2">java</prop>
                <prop key="properties3">java</prop>
            </props>
        </property>
        <!--陣列配置-->
        <property name="arr">
            <array>
                <value>11</value>
                <value>12</value>
                <value>13</value>
            </array>
        </property>
        <!--hashmap集合配置-->
        <property name="hmap">
            <map>
                <entry key="map1" value="Laity1"></entry>
                <entry key="map2" value="Laity2"></entry>
            </map>
        </property>
        <!--hashset集合配置-->
        <property name="hset">
            <set>
                <value>hset1</value>
                <value>hset2</value>
                <value>hset3</value>
            </set>
        </property>
    </bean>

</beans>
  1. 執行結果

三、參考資料

理解Spring中的IoC和DI

控制反轉(IoC)與依賴注入(DI)