一是钥、Ioc
IoC是控制反轉(zhuǎn)缅叠,通俗講就是把對象創(chuàng)建和對象之間的調(diào)用交給Spring管理肤粱,通過簡單的xml配置(或者注解、Java8)就可以創(chuàng)建和調(diào)用對象鸥鹉,其主要目的就是解耦毁渗,降低代碼之間的耦合度
代碼之間解耦的方式
接口解耦: 使得UserService和UserDao1, UserDao2之間的聯(lián)系沒那么緊密,Dao都實現(xiàn)了UserDao接口单刁。這是一種弱耦合的關(guān)系灸异。但是會發(fā)現(xiàn)接口和實現(xiàn)類中有強耦合關(guān)系了。比如UserDao dao = new UserDao2();
工廠模式解耦:所謂的工廠模式也就是將所有的創(chuàng)建對象任務(wù)交給了一個“中間人”,也就是工廠類來實現(xiàn)肺樟,要想使用對象檐春,直接找工廠類,實現(xiàn)類必須要從工廠中取出來儡嘶。
創(chuàng)建對象只需要調(diào)用工廠類BeanFactory中的方法即可喇聊,調(diào)用時不是直接通過接口恍风,而是通過工廠類蹦狂,將創(chuàng)建對象交給了工廠類,就降低了接口和實現(xiàn)類之間的耦合
但是朋贬,這樣接口和工廠類之間就產(chǎn)生了耦合凯楔,為了再次解耦,我們引入了反射+xml配置文件的方式進行再次解耦xml配置+反射+工廠解耦(IoC底層的實現(xiàn))
<bean id="userDao" class="**.UserDaoImpl">
class BeanFactory {
public static UserDao getUserDao(String id) {
// String className = 解析配置文件xml 拿到id對應(yīng)的class
// 反射
class clazz = class.forName(className);
return clazz.newInstance();
}
}
沒有直接像上面那樣直接new對象,而是使用了xml解析和反射方式創(chuàng)建對象廷没,分析如下:
- 通過xml解析獲取對象中屬性的值
- 通過反射得到字節(jié)碼文件
- 通過字節(jié)碼文件創(chuàng)建對象
這樣實現(xiàn)改UserDao實現(xiàn)類型,只要改xml文件(后面是改配置類)狭归,實現(xiàn)解耦
- Ioc的解讀
Ioc是一種思想,將設(shè)計好的對象交給容器疚宇,不是傳統(tǒng)的對象內(nèi)部直接控制。
- 傳統(tǒng)的Java是主動創(chuàng)建對象讼撒,Ioc是有專門的容器來創(chuàng)建這些對象钳幅,即由IoC容器來控制對 象的創(chuàng)建
- IoC容器控制了對象册赛;主要控制了外部資源獲取
- 反轉(zhuǎn)是由容器來幫忙創(chuàng)建及注入依賴對象
- 因為由容器幫我們查找及注入依賴對象牡属,對象只是被動的接受依賴對象,所以是反轉(zhuǎn).依賴對象的獲取被反轉(zhuǎn)了
- Ioc能做什么
- 有了IoC容器后措伐,把創(chuàng)建和查找依賴對象的控制權(quán)交給了容器,由容器進行注入組合對象,所以對象與對象之間是 松散耦合氢架,
這樣也方便測試,利于功能復(fù)用孙援,更重要的是使得程序的整個體系結(jié)構(gòu)變得非常靈活 - 應(yīng)用程序原本是老大,要獲取什么資源都是主動出擊础淤,但是在IoC/DI思想中币砂,應(yīng)用程序就變成被動的了,
被動的等待IoC容器來創(chuàng)建并注入它所需要的資源了掌桩。 - IoC很好的體現(xiàn)了面向?qū)ο笤O(shè)計法則之一—— 好萊塢法則:“別找我們,我們找你”灰蛙;
即由IoC容器幫對象找相應(yīng)的依賴對象并注入物延,而不是由對象主動去找
- DI(依賴注入)
- 組件之間依賴關(guān)系由容器在運行期決定笙纤,形象的說抖拴,即由容器動態(tài)的將某個依賴關(guān)系注入到組件之中
- 依賴注入的目的并非為軟件系統(tǒng)帶來更多功能,而是為了提升組件重用的頻率,并為系統(tǒng)搭建一個靈活往湿、可擴展的平臺
- 通過依賴注入機制,我們只需要通過簡單的配置矩乐,而無需任何代碼就可指定目標需要的資源,完成自身的業(yè)務(wù)邏輯欧漱,而不需要關(guān)心具體的資源來自何處谱净,由誰實現(xiàn)
誰依賴于誰:當然是應(yīng)用程序依賴于IoC容器;
為什么需要依賴:應(yīng)用程序需要IoC容器來提供對象需要的外部資源;
誰注入誰:很明顯是IoC容器注入應(yīng)用程序某個對象,應(yīng)用程序依賴的對象较幌;
注入了什么:就是注入某個對象所需要的外部資源(包括對象嘁字、資源、常量數(shù)據(jù))
- IoC和DI有什么關(guān)系
它們是同一個概念的不同角度描述灭袁,由于控制反轉(zhuǎn)概念比較含糊(可能只是理解為容器控制對象這一個層面显沈,很難讓人想到誰來維護對象關(guān)系),
所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”只锭,相對IoC 而言,“依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”
依賴注入的意思是你需要的東西不是由你創(chuàng)建的,而是第三方,或者說容器提供給你的贞盯。這樣的設(shè)計符合正交性,即所謂的松耦合
二件余、AOP
面向切面編程,指在程序運行期間動態(tài)的將某段代碼切入到指定方法指定位置進行運行的操作损谦。如:性能監(jiān)控岖免、日志記錄、權(quán)限控制等颅湘,
通過AOP解決代碼耦合問題闯参,讓職責更加單一動態(tài)代理增強
- 動態(tài)代理,需要一個代理類释移,這個代理類是動態(tài)生成的,字節(jié)碼要用的時候就創(chuàng)建叮盘,要用的時候就加載觅玻,在不修改源碼的基礎(chǔ)上對方法進行增強
- 有兩種代理機制僻爽,一種是基于JDK的動態(tài)代理贾惦,另一種是基于CGLib的動態(tài)代理胸梆。其中JDK代理必須要實現(xiàn)了接口才行。
spring aop 的底層须板,也是使用的動態(tài)代理
// CGLib
final UserService bean = new UserService();
UserService cglibProducer = (UserService) Enhancer.create(bean.getClass(), new MethodInterceptor(){
/**
* 作用:執(zhí)行被代理對象的任何借口方法都會經(jīng)過該方法
* @param proxy:代理對象的引用
* @param method:當前執(zhí)行的方法
* @param args:當前執(zhí)行方法所需的參數(shù)
* @return:和被代理對象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("記錄日志");
Object result = method.invoke(bean, args);
return result;
}
});
- 啟用AOP的步驟
- @EnableAspectJAutoProxy注解 開啟
- @Aspect注解聲明一個切面碰镜,@Pointcut()聲明切點,并使用@Around习瑰、@Before绪颖、@After等注解表明連接點
- 簡單的來說,@Aspect告訴系統(tǒng)這里是切面了甜奄,@Pointcut表示切在哪里(比如某個類里的所有方法), @Around表示怎么切柠横,是方法前后切,方法后切等
- APO術(shù)語
- 連接點(Joinpoint): 比如某個方法調(diào)用前课兄、調(diào)用后牍氛、拋出異常后,某個邊界性質(zhì)(某個時機)烟阐,如@Before搬俊,@Around
- 切點(Pointcut):指的是連接點的集合,我理解就是切入在哪里蜒茄。通過切點定位到在哪里執(zhí)行悠抹,通過連接點確定執(zhí)行時機
- 通知(Advice): 簡單的說,就是需要做什么扩淀,就是在連接點的基礎(chǔ)上
- 目標對象:被增強的對象
- 引介:允許我們向現(xiàn)有的類添加新方法屬性
- 織入:織入是將通知添加到目標類具體連接點上的過程。有三種織入方式:
動態(tài)代理織入(在運行期為目標類添加通知生成子類的方式)啤挎; 類裝載織入(要求使用特殊的類裝載器)驻谆; 編譯期織入(使用特殊的Java編譯器);庆聘、
Spring用的動態(tài)代理胜臊,AspectJ使用的是編譯織入和類裝載織入 - 代理對象(Proxy): AOP織入通知后,會產(chǎn)出一個結(jié)果類伙判,就是代理對象
- 切面(AspectJ): 連接點和切點以及通知所在的那個類稱為切面
- AOP原理