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核心介面
BeanFactory
和ApplicationContext
是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思想,這將會在下一篇部落格種總結分享。
以上內容均是自主學習總結,如有不適之處歡迎留言指正。
感謝閱讀!