原文地址:
https://gyl-coder.top/spring/spring-ioc-aop/ 。
這篇文章會(huì)從下面從以下幾個(gè)問題展開對(duì) IoC & AOP 的解釋
- 什么是 IoC?
- IoC 解決了什么問題?
- IoC 和 DI 的區(qū)別?
- 什么是 AOP?
- AOP 解決了什么問題?
- AOP 為什么叫做切面編程?
首先聲明:IoC & AOP 不是 Spring 提出來的,它們?cè)?Spring 之前其實(shí)已經(jīng)存在了,只不過當(dāng)時(shí)更加偏向于理論。Spring 在技術(shù)層次將這兩個(gè)思想進(jìn)行了很好的實(shí)現(xiàn)。
什么是 IoC
IoC (Inversion of control )控制反轉(zhuǎn)/反轉(zhuǎn)控制。它是一種思想不是一個(gè)技術(shù)實(shí)現(xiàn)。描述的是:JAVA 開發(fā)領(lǐng)域?qū)ο蟮膭?chuàng)建以及管理的問題。
例如:現(xiàn)有類 A 依賴于類 B
- 傳統(tǒng)的開發(fā)方式 :往往是在類 A 中手動(dòng)通過 new 關(guān)鍵字來 new 一個(gè) B 的對(duì)象出來
- 使用 IoC 思想的開發(fā)方式 :不通過 new 關(guān)鍵字來創(chuàng)建對(duì)象,而是通過 IoC 容器(Spring 框架) 來幫助我們實(shí)例化對(duì)象。我們需要哪個(gè)對(duì)象,直接從 IoC 容器里面過去即可。
從以上兩種開發(fā)方式的對(duì)比來看:我們 “喪失了一個(gè)權(quán)力” (創(chuàng)建、管理對(duì)象的權(quán)力),從而也得到了一個(gè)好處(不用再考慮對(duì)象的創(chuàng)建、管理等一系列的事情)
為什么叫控制反轉(zhuǎn)
控制 :指的是對(duì)象創(chuàng)建(實(shí)例化、管理)的權(quán)力
反轉(zhuǎn) :控制權(quán)交給外部環(huán)境(Spring 框架、IoC 容器)
IoC 解決了什么問題
IoC 的思想就是兩方之間不互相依賴,由第三方容器來管理相關(guān)資源。這樣有什么好處呢?
- 對(duì)象之間的耦合度或者說依賴程度降低;
- 資源變的容易管理;比如你用 Spring 容器提供的話很容易就可以實(shí)現(xiàn)一個(gè)單例。
例如:現(xiàn)有一個(gè)針對(duì) User 的操作,利用 Service 和 Dao 兩層結(jié)構(gòu)進(jìn)行開發(fā)
在沒有使用 IoC 思想的情況下,Service 層想要使用 Dao 層的具體實(shí)現(xiàn)的話,需要通過 new 關(guān)鍵字在UserServiceImpl 中手動(dòng) new 出 IUserDao 的具體實(shí)現(xiàn)類 UserDaoImpl(不能直接 new 接口類)。
很完美,這種方式也是可以實(shí)現(xiàn)的,但是我們想象一下如下場景:
開發(fā)過程中突然接到一個(gè)新的需求,針對(duì)對(duì)IUserDao 接口開發(fā)出另一個(gè)具體實(shí)現(xiàn)類。因?yàn)?Server 層依賴了IUserDao的具體實(shí)現(xiàn),所以我們需要修改UserServiceImpl中 new 的對(duì)象。如果只有一個(gè)類引用了IUserDao的具體實(shí)現(xiàn),可能覺得還好,修改起來也不是很費(fèi)力氣,但是如果有許許多多的地方都引用了IUserDao的具體實(shí)現(xiàn)的話,一旦需要更換IUserDao 的實(shí)現(xiàn)方式,那修改起來將會(huì)非常的頭疼。
使用 IoC 的思想,我們將對(duì)象的控制權(quán)(創(chuàng)建、管理)交有 IoC 容器去管理,我們?cè)谑褂玫臅r(shí)候直接向 IoC 容器 “要” 就可以了
IoC 和 DI 別再傻傻分不清楚
IoC(Inverse of Control:控制反轉(zhuǎn))是一種設(shè)計(jì)思想 或者說是某種模式。這個(gè)設(shè)計(jì)思想就是 將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán),交由 Spring 框架來管理。IoC 在其他語言中也有應(yīng)用,并非 Spring 特有。IoC 容器是 Spring 用來實(shí)現(xiàn) IoC 的載體, IoC 容器實(shí)際上就是個(gè) Map(key,value),Map 中存放的是各種對(duì)象。
IoC 最常見以及最合理的實(shí)現(xiàn)方式叫做依賴注入(Dependency Injection,簡稱 DI)。
并且,老馬(Martin Fowler)在一篇文章中提到將 IoC 改名為 DI,原文如下,原文地址:
https://martinfowler.com/articles/injection.html 。
老馬的大概意思是 IoC 太普遍并且不表意,很多人會(huì)因此而迷惑,所以,使用 DI 來精確指名這個(gè)模式比較好。
什么是 AOP
AOP:Aspect oriented programming 面向切面編程,AOP 是 OOP(面向?qū)ο缶幊蹋┑囊环N延續(xù)。
下面我們先看一個(gè) OOP 的例子。
例如:現(xiàn)有三個(gè)類,Horse、Pig、Dog,這三個(gè)類中都有 eat 和 run 兩個(gè)方法。
通過 OOP 思想中的繼承,我們可以提取出一個(gè) Animal 的父類,然后將 eat 和 run 方法放入父類中,Horse、Pig、Dog通過繼承Animal類即可自動(dòng)獲得eat() 和 run() 方法。這樣將會(huì)少些很多重復(fù)的代碼。
OOP 編程思想可以解決大部分的代碼重復(fù)問題。但是有一些問題是處理不了的。比如在父類 Animal 中的多個(gè)方法的相同位置出現(xiàn)了重復(fù)的代碼,OOP 就解決不了。
/**
* 動(dòng)物父類
*/
public class Animal {
/** 身高 */
private String height;
/** 體重 */
private double weight;
public void eat() {
// 性能監(jiān)控代碼
long start = System.currentTimeMillis();
// 業(yè)務(wù)邏輯代碼
System.out.println("I can eat...");
// 性能監(jiān)控代碼
System.out.println("執(zhí)行時(shí)長:" + (System.currentTimeMillis() - start)/1000f + "s");
}
public void run() {
// 性能監(jiān)控代碼
long start = System.currentTimeMillis();
// 業(yè)務(wù)邏輯代碼
System.out.println("I can run...");
// 性能監(jiān)控代碼
System.out.println("執(zhí)行時(shí)長:" + (System.currentTimeMillis() - start)/1000f + "s");
}
}
這部分重復(fù)的代碼,一般統(tǒng)稱為 橫切邏輯代碼。
橫切邏輯代碼存在的問題:
- 代碼重復(fù)問題
- 橫切邏輯代碼和業(yè)務(wù)代碼混雜在一起,代碼臃腫,不便維護(hù)
AOP 就是用來解決這些問題的
AOP 另辟蹊徑,提出橫向抽取機(jī)制,將橫切邏輯代碼和業(yè)務(wù)邏輯代碼分離
代碼拆分比較容易,難的是如何在不改變?cè)袠I(yè)務(wù)邏輯的情況下,悄無聲息的將橫向邏輯代碼應(yīng)用到原有的業(yè)務(wù)邏輯中,達(dá)到和原來一樣的效果。
AOP 解決了什么問題
通過上面的分析可以發(fā)現(xiàn),AOP 主要用來解決:在不改變?cè)袠I(yè)務(wù)邏輯的情況下,增強(qiáng)橫切邏輯代碼,根本上解耦合,避免橫切邏輯代碼重復(fù)。
AOP 為什么叫面向切面編程
切 :指的是橫切邏輯,原有業(yè)務(wù)邏輯代碼不動(dòng),只能操作橫切邏輯代碼,所以面向橫切邏輯
面 :橫切邏輯代碼往往要影響的是很多個(gè)方法,每個(gè)方法如同一個(gè)點(diǎn),多個(gè)點(diǎn)構(gòu)成一個(gè)面。這里有一個(gè)面的概念