IOC : Inversion of Control
(1). IoC(Inversion of Control)是指容器控制程序?qū)ο笾g的關(guān)系燕偶,而不是傳統(tǒng)實(shí)現(xiàn)中景埃,由程序代碼直接操控”允控制權(quán)由應(yīng)用代碼中轉(zhuǎn)到了外部容器,控制權(quán)的轉(zhuǎn)移是所謂反轉(zhuǎn)。 對(duì)于Spring而言砖瞧,就是由Spring來控制對(duì)象的生命周期和對(duì)象之間的關(guān)系;IoC還有另外一個(gè)名字——“依賴注入(Dependency Injection)”。從名字上理解弥搞,所謂依賴注入她按,即組件之間的依賴關(guān)系由容器在運(yùn)行期決定,即由容器動(dòng)態(tài)地將某種依賴關(guān)系注入到組件之中竭翠。
(2). 在Spring的工作方式中,所有的類都會(huì)在spring容器中登記薇搁,告訴spring這是個(gè)什么東西斋扰,你需要什么東西,然后spring會(huì)在系統(tǒng)運(yùn)行到適當(dāng)?shù)臅r(shí)候啃洋,把你要的東西主動(dòng)給你传货,同時(shí)也把你交給其他需要你的東西。所有的類的創(chuàng)建宏娄、銷毀都由 spring來控制问裕,也就是說控制對(duì)象生存周期的不再是引用它的對(duì)象,而是spring孵坚。對(duì)于某個(gè)具體的對(duì)象而言僻澎,以前是它控制其他對(duì)象,現(xiàn)在是所有對(duì)象都被spring控制十饥,所以這叫控制反轉(zhuǎn)窟勃。
(3). 在系統(tǒng)運(yùn)行中,動(dòng)態(tài)的向某個(gè)對(duì)象提供它所需要的其他對(duì)象逗堵。
(4). 依賴注入的思想是通過反射機(jī)制實(shí)現(xiàn)的秉氧,在實(shí)例化一個(gè)類時(shí),它通過反射調(diào)用類中set方法將事先保存在HashMap中的類屬性注入到類中蜒秤。 總而言之汁咏,在傳統(tǒng)的對(duì)象創(chuàng)建方式中亚斋,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實(shí)例,而在Spring中創(chuàng)建被調(diào)用者的工作由Spring來完成攘滩,然后注入調(diào)用者帅刊,即所謂的依賴注入or控制反轉(zhuǎn)。 注入方式有兩種:依賴注入和設(shè)置注入漂问; IoC的優(yōu)點(diǎn):降低了組件之間的耦合赖瞒,降低了業(yè)務(wù)對(duì)象之間替換的復(fù)雜性,使之能夠靈活的管理對(duì)象蚤假。
AOP(Aspect Oriented Programming)
(1). AOP面向方面編程基于IoC栏饮,是對(duì)OOP的有益補(bǔ)充;
(2). AOP利用一種稱為“橫切”的技術(shù)磷仰,剖解開封裝的對(duì)象內(nèi)部袍嬉,并將那些影響了 多個(gè)類的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”灶平,即方面伺通。所謂“方面”,簡(jiǎn)單地說逢享,就是將那些與業(yè)務(wù)無關(guān)泵殴,卻為業(yè)務(wù)模塊所共同調(diào)用的 邏輯或責(zé)任封裝起來,比如日志記錄拼苍,便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度调缨,并有利于未來的可操作性和可維護(hù)性疮鲫。
(3). AOP代表的是一個(gè)橫向的關(guān) 系,將“對(duì)象”比作一個(gè)空心的圓柱體弦叶,其中封裝的是對(duì)象的屬性和行為俊犯;則面向方面編程的方法,就是將這個(gè)圓柱體以切面形式剖開伤哺,選擇性的提供業(yè)務(wù)邏輯燕侠。而 剖開的切面,也就是所謂的“方面”了立莉。然后它又以巧奪天功的妙手將這些剖開的切面復(fù)原绢彤,不留痕跡,但完成了效果蜓耻。
(4). 實(shí)現(xiàn)AOP的技術(shù)茫舶,主要分為兩大類:一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式刹淌,對(duì)該消息進(jìn)行裝飾饶氏,以取代原有對(duì)象行為的執(zhí)行讥耗;二是采用靜態(tài)織入的方式,引入特定的語法創(chuàng)建“方面”疹启,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼古程。
(5). Spring實(shí)現(xiàn)AOP:JDK動(dòng)態(tài)代理和CGLIB代理 JDK動(dòng)態(tài)代理:其代理對(duì)象必須是某個(gè)接口的實(shí)現(xiàn),它是通過在運(yùn)行期間創(chuàng)建一個(gè)接口的實(shí)現(xiàn)類來完成對(duì)目標(biāo)對(duì)象的代理喊崖;其核心的兩個(gè)類是InvocationHandler和Proxy挣磨。 CGLIB代理:實(shí)現(xiàn)原理類似于JDK動(dòng)態(tài)代理,只是它在運(yùn)行期間生成的代理對(duì)象是針對(duì)目標(biāo)類擴(kuò)展的子類贷祈。CGLIB是高效的代碼生成包趋急,底層是依靠ASM(開源的java字節(jié)碼編輯類庫)操作字節(jié)碼實(shí)現(xiàn)的,性能比JDK強(qiáng)势誊;需要引入包asm.jar和cglib.jar呜达。 使用AspectJ注入式切面和@AspectJ注解驅(qū)動(dòng)的切面實(shí)際上底層也是通過動(dòng)態(tài)代理實(shí)現(xiàn)的。
(6). AOP使用場(chǎng)景:
Authentication 權(quán)限檢查
Caching 緩存
Context passing 內(nèi)容傳遞
Error handling 錯(cuò)誤處理
Lazy loading 延遲加載
Debugging 調(diào)試
logging, tracing, profiling and monitoring 日志記錄粟耻,跟蹤查近,優(yōu)化,校準(zhǔn)
Performance optimization 性能優(yōu)化挤忙,效率檢查
Persistence 持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務(wù)管理
代理模式:
代理(Proxy)是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪問方式;即通過代理對(duì)象訪問目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴(kuò)展該方法霜威。
代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象。
1.1.靜態(tài)代理
靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.
/**
* 接口
*/
public interface IUserDao {
void save();
}
/**
* 接口實(shí)現(xiàn)
* 目標(biāo)對(duì)象
*/
public class UserDao implements IUserDao {
public void save() {
System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
}
}
/**
* 代理對(duì)象,靜態(tài)代理
*/
public class UserDaoProxy implements IUserDao{
//接收保存目標(biāo)對(duì)象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("開始事務(wù)...");
target.save();//執(zhí)行目標(biāo)對(duì)象的方法
System.out.println("提交事務(wù)...");
}
}
/**
* 測(cè)試類
*/
public class App {
public static void main(String[] args) {
//目標(biāo)對(duì)象
UserDao target = new UserDao();
//代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();//執(zhí)行的是代理的方法
}
}
1.2.動(dòng)態(tài)代理
動(dòng)態(tài)代理有以下特點(diǎn):
1.代理對(duì)象,不需要實(shí)現(xiàn)接口
2.代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
3.動(dòng)態(tài)代理也叫做:JDK代理,接口代理
接口類IUserDao.java以及接口實(shí)現(xiàn)類,目標(biāo)對(duì)象UserDao是一樣的,沒有做修改.在這個(gè)基礎(chǔ)上,增加一個(gè)代理工廠類(ProxyFactory.java),將代理類寫在這個(gè)地方,然后在測(cè)試類(需要使用到代理的代碼)中先建立目標(biāo)對(duì)象和代理對(duì)象的聯(lián)系,然后代用代理對(duì)象的中同名方法
/**
* 創(chuàng)建動(dòng)態(tài)代理對(duì)象
* 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類型
*/
public class ProxyFactory{
//維護(hù)一個(gè)目標(biāo)對(duì)象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//給目標(biāo)對(duì)象生成代理對(duì)象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始事務(wù)2");
//執(zhí)行目標(biāo)對(duì)象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務(wù)2");
return returnValue;
}
}
);
}
}
注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:
ClassLoader loader,:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的
Class<?>[] interfaces,:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
/**
* 測(cè)試類
*/
public class App {
public static void main(String[] args) {
// 目標(biāo)對(duì)象
IUserDao target = new UserDao();
// 【原始的類型 class cn.itcast.b_dynamic.UserDao】
System.out.println(target.getClass());
// 給目標(biāo)對(duì)象册烈,創(chuàng)建代理對(duì)象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// class $Proxy0 內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象
System.out.println(proxy.getClass());
// 執(zhí)行方法 【代理對(duì)象】
proxy.save();
}
}
總結(jié):
代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理
1.3.Cglib代理
上面的靜態(tài)代理和動(dòng)態(tài)代理模式都是要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口的目標(biāo)對(duì)象,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用以目標(biāo)對(duì)象子類的方式類實(shí)現(xiàn)代理,這種方法就叫做:Cglib代理
Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.
JDK的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果想代理沒有實(shí)現(xiàn)接口的類,就可以使用Cglib實(shí)現(xiàn).
Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類與實(shí)現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
Cglib包的底層是通過使用一個(gè)小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵(lì)直接使用ASM,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
Cglib子類代理實(shí)現(xiàn)方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已經(jīng)包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在內(nèi)存中動(dòng)態(tài)構(gòu)建子類
3.代理的類不能為final,否則報(bào)錯(cuò)
4.目標(biāo)對(duì)象的方法如果為final/static,那么就不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法.
/**
* 目標(biāo)對(duì)象,沒有實(shí)現(xiàn)任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
}
}
/**
* Cglib子類代理工廠
* 對(duì)UserDao在內(nèi)存中動(dòng)態(tài)構(gòu)建一個(gè)子類對(duì)象
*/
public class ProxyFactory implements MethodInterceptor{
//維護(hù)目標(biāo)對(duì)象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象
public Object getProxyInstance(){
//1.工具類
Enhancer en = new Enhancer();
//2.設(shè)置父類
en.setSuperclass(target.getClass());
//3.設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//4.創(chuàng)建子類(代理對(duì)象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開始事務(wù)...");
//執(zhí)行目標(biāo)對(duì)象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務(wù)...");
return returnValue;
}
}
在Spring的AOP編程中:
如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,用JDK代理
如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,用Cglib代理