Spring之IOC

  • 2019 年 10 月 3 日
  • 筆記

一,前言

​ 本篇部落格分享一些關於Spring中一個核心概念,IOC。

IOC:Inversion of Control ,控制反轉。

​ 通常情況下對於實例化一個對象,我們會通過關鍵字new創建出來。但是在實際項目開發中不可能有一個實例化對象,而多個對象就需要多個new創建。顯然,這勢必造成多個對象之間的耦合,以及對程式的維護性帶來困難。

控制反轉 ,顧名思義就是將控制權力交給Spring容器。簡單理解就是將創建bean的工作交給Spring來做,對於開發人員來說就不需要再手動創建。並且Spring容器會自動維護bean之間的關係,及bean的生命周期等,大大降低了程式之間的耦合。

​ 接著再說說和IOC相關的另一個概念:

DI:Dependency Injection,依賴注入。

依賴注入,在程式運行時,動態的向對象中注入它所依賴的對象。舉例,A對象依賴B對象中的某些屬性或方法,但是在Spring容器創建A對象之前,並不知道A的依賴關係。因此在真正創建A對象時,發現依賴了B對象,則此時也會將B對象創建出來並注入到A中,這就是依賴注入。

我個人的理解就是DI是IOC的一種體現方式,這也是DI和IOC兩者的區別。

二,IOC核心介面

BeanFactoryApplicationContext是IOC容器體現形態的兩大核心介面,請看下圖兩者之間的聯繫。

​ 兩者之間是繼承關係,BeanFactory作為最頂層介面,裡面只實現了容器的基本功能,是一種簡單容器。而ApplicationContext作為最底層的子類,則自然擁有父類的基本功能,並且還增加了很多面向框架的特性,如國際化,時間發布等,是一種高級容器

ApplicationContext:
​ 它在構建容器時,創建對象採用的策略是採用立即載入的方式,也就是說當讀取完配置文件時,容器就是馬上創建對象。
BeanFactory:
​ 它在構建核心容器時,創建對象採取的策略是延時載入,什麼時候根據id獲取對象時,容器才會真正創建對象。

​ 這裡我們只總結關於ApplicationContext介面的使用。

三,ApplicationContext

ApplicationContext是一個介面,那麼要想使用該介面就要實例化它的實現類,請看如下所示:

​ 其中常用的三個實現類為:

ClassPathXmlApplicationContext:載入類路徑下的配置文件。
FileSystemXmlApplicationContext:載入磁碟中任意位置的配置文件(要有訪問許可權)
AnnotationConfigApplicationContext:讀取註解標記。

​ 以上三種都是獲取bean的實例化對象,那麼我們又如何定義bean對象呢,同樣也是有三種方式。

第一種:使用默認的構造函數創建

​ 在spring的配置文件中使用bean標籤,配置id和class屬性使用就是默認的構造函數創建的對象。

​ 注意:如果類中沒有無參構造函數,則創建對象失敗。

​ 首先定義bean.cml文件:

<?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.frame.service.impl.UserServiceImpl"></bean>    </beans>

​ 定義介面IUserService的實現類:

public class UserServiceImpl implements IUserService {      /**       * 無參構造       */      public UserServiceImpl() {          System.out.println("創建對象");      }      @Override      public void addUser() {          System.out.println("添加用戶");      }  }

​ 主方法中獲取該實例對象:

 public class Demo {      public static void main(String[] args) {      // 1,載入類路徑下的配置文件      ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");      // 2,通過getBean()獲取實例化對象      IUserService userService = (IUserService) ac.getBean("userService");      // 3,通過bean對象調用方法      userService.addUser();      }   }

​ 運行結果為:

第二種,普通工廠模式

​ 使用類中的方法創建對象,並注入到spring容器中。

思路:模擬一個工廠,創建並返回上述實現類對象。

// 模擬bean工廠  public class InstanceBean {      public IUserService userservice(){          return new UserServiceImpl();      }  }
<bean id="instanceBean" class="com.frame.factory.InstanceBean"></bean>      <bean id="userService" factory-bean="instanceBean" factory-method="userservice"></bean>

​ 而主方法中的程式碼和上面是一樣的,這種創建bean的方式比較靈活,通過配置文件去載入自定義的工廠。

第三種,普通工廠模式中的靜態方法

// 將方法用static修飾  public class StaticInstanceBean {      public static IUserService userservice(){          return new UserServiceImpl();      }  }
<bean id="userService" class="com.frame.factory.StaticInstanceBean" factory-method="userservice"></bean>

四,Bean的作用範圍

​ 通過IOC可以將創建對象這個事交給Spring容器,那麼Bean的作用範圍容器是怎麼定義的。

bean標籤的scope屬性:
singleton:單例模式(默認使用)
prototype:多例模式
request:(Spring2.0之後)web應用的請求範圍
session:(Spring2.0之後)web用用的會話範圍
global-session:(Spring2.0之後)全局會話範圍。

​ 在Spring容器中,默認是使用單例模式,如下程式碼:

<bean id="userService" class="com.frame.service.impl.UserServiceImpl" scope="singleton"></bean>
// 1,載入配置文件  ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");  // 2,獲取Bean對象  IUserService userService = (IUserService) ac.getBean("userService");  // 3,再次獲取  IUserService userService1 = (IUserService) ac.getBean("userService");          System.out.println(userService == userService1);  

​ 運行結果為:

​ 證明多次獲取Bean對象,在單例模式下是同一個對象。同樣,將配置文件中scope值改為多例模式,如下程式碼:

<bean id="userService" class="com.frame.service.impl.UserServiceImpl" scope="prototype"></bean>

​ 主方法中的程式碼同上,那麼我們直接看運行結果:

​ 很明顯,在多例模式下IOC創建了兩次對象。

五,DI

​ 上面我們總結了DI的概念,那麼依賴注入都能注入什麼類型呢,又可以使用什麼方式進行進入呢。

注入的數據有三類:
​ 1,基本數據類型
2,其他bean類型
3,複雜類型(Array,List,Map,Set等)
注入的三種方式:
1,使用函數構造
​ 2,使用set方法提供
​ 3,使用註解提供

第一種,使用函數構造

public class UserServiceImpl{      private Integer id;      private String name;      private Date birthday;        public UserServiceImpl(){}      public UserServiceImpl(Integer id,String name,Date birthday) {          this.id = id;          this.name = name;          this.birthday = birthday;      }    }
<bean id="userService" class="com.frame.service.impl.UserServiceImpl">          <constructor-arg name="id" value="10010"></constructor-arg>          <constructor-arg name="name" value="小明"></constructor-arg>          <constructor-arg name="birthday" ref="now"></constructor-arg>      </bean>  <!-- 聲明Date對象 -->       <bean id="now" class="java.util.Date"></bean>

​ 標籤中屬性使用:

  • type:指定要注入數據的數據類型。
  • index:指定要注入的數據的索引位置的參數進行賦值,索引值從0開始。
  • name:指定給構造函數中指定名稱的參數賦值。
  • value:提供基本類型和String類型的數據。
  • ref:引用其他bean類型數據。

第二種,使用set方法提供

public class UserServiceImpl{        private Integer id;      private String name;      private Date birthday;        public void setId(Integer id) {          this.id = id;      }        public void setName(String name) {          this.name = name;      }        public void setBirthday(Date birthday) {          this.birthday = birthday;      }  }
<bean id="userService" class="com.frame.service.impl.UserServiceImpl">          <property name="id" value="10010"></property>          <property name="name" value="張三"></property>          <property name="birthday" ref="now"></property>      </bean>  <!-- 聲明Date對象 -->       <bean id="now" class="java.util.Date"></bean>

​ 標籤的屬性:

  • name:指定注入時set方法名稱。
  • value:提供基本類型和String類型的數據。
  • ref:引用其他bean類型數據。

第三種,註解形式

@Autowired:自動注入

@Qualifier:與@Autowired搭配使用,按照名稱進行注入。

@Resource:通過id進行注入,可單獨使用。

注意:這三種只能注入bean類型。

@Value:用於注入基本類型和String類型的數據。

六,總結

​ 本篇部落格是幫助理解IOC的概念,通過上述總結能有一個更好更簡單的理解。對於Spring還有一個核心概念就是AOP思想,這將會在下一篇部落格種總結分享。

​ 以上內容均是自主學習總結,如有不適之處歡迎留言指正。

感謝閱讀!