一: 代理模式
靜態代理:代理模式
原始對象
代理對象:對原始對象的方法做了增強
動態代理
jdk的動態代理
本質:在內存中構建出接口的實現類
特點:被代理對象,必須有接口
public void jdkTest(){
//如何生成代理對象?
final UserDAO dao=new UserDAOImpl();
//1參數 :被代理對象 UserDAOImpl的類加載器
//2參數 :被代對象 所屬的接口
UserDAO proxy=(UserDAO)Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
/**
* proxy:代理對象
*
* method:代理對象的方法 add
*
* args:代理對象的方法的參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("開啟事務");
//真正的dao.add()
Object invoke = method.invoke(dao, args);
System.out.println("記錄日志");
return invoke;
}
});
proxy.add();
}
cglib動態代理
本質:在內存中生成被代理對象的【子類】
特點:可以在沒有接口的情況下代理
對於不使用接口的業務類,無法使用JDK動態代理,cglib采用非常底層的字節碼技術,可以為一個類創建
子類,解決無接口代理問題
動態代理和靜態代理區別??
解析:靜態代理需要手工編寫代理類,代理類引用被代理對象。
動態代理是在內存中構建的,不需要手動編寫代理類
代理的目的:是為了在原有的方法上進行增強。
二:靜態代理(具體代碼)
①定義接口
//抽象主題 public interface Subject { public String request(); }
② 定義接口的實現類 RealSubject並實現該接口,重寫方法--被代理對象
package cn.happy.proxy; public class RealSubject implements Subject { @Override public String request() { System.out.println("真實主題的操作內容"); return "呵呵"; } }
③定義接口的實現類ProxySubject並實現該接口重寫方法。自定義屬性RealSubject,調用request方法,在這里進行增強
package cn.happy.proxy; public class ProxySubject implements Subject { //01.植入一個 真實主題的引用 private RealSubject real; @Override public String request() { System.out.println("before execute"); real.request(); System.out.println("after execute"); return "proxy"; } public RealSubject getReal() { return real; } public void setReal(RealSubject real) { this.real = real; } }
④測試類
//靜態代理 public class Spring_01Test { @Test public void proxyTest(){ //准備一個真實主題,被代理對象 RealSubject real=new RealSubject(); //02.創建一個代理對象 ProxySubject proxy=new ProxySubject(); proxy.setReal(real); proxy.request(); } }
執行效果:
三、動態代理
1.JDK動態代理:(具體代碼)
定義接口IUserDao:
//接口 public interface IUserDAO { public String add(); public String edit(); }
定義接口實現類,實現某接口,並重寫該方法:
public class UserDAOImpl implements IUserDAO { public String add() { //開啟事務 System.out.println("add ok!"); return "add"; //logger } public String edit() { //開啟事務 System.out.println("edit ok!"); return "edit"; //開啟事務 } }
測試類:(重點)
//jdk動態代理測試 public class MyTest { @Test /** * jdk動態代理測試 */ public void staticproxyTest(){ //01.先創建出接口實現類 final IUserDAO dao=new UserDAOImpl(); //02.類Proxy IUserDAO proxy=(IUserDAO)Proxy // .newProxyInstance(dao.getClass() //獲取實現類的類加載器 .getClassLoader(), dao.getClass(). // getInterfaces(), // 獲取實現類接口 new InvocationHandler() { //Invocation(調用 ) Handler (處理) /**proxy 代理對象 * method 被代理對象 方法 add() * args add方法的參數 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //code 1 System.out.println("start Tran"); //真正業務 //執行dao對象的add方法,傳入的是args參數,返回值是result Object result = method.invoke(dao, args); //code 2 System.out.println("write logger"); return result; } }); String add = proxy.add(); System.out.println("======="+add+"=======");//"add" proxy.edit(); } }
實現效果:
可以看到,動態生成的代理類有如下特性:
1) 繼承了Proxy類,實現了代理的接口,由於java不能多繼承,這里已經繼承了Proxy類了,不能再繼承其他的類,所以JDK的動態代理不支持對實現類的代理,只支持接口的代理。
2) 提供了一個使用InvocationHandler作為參數的構造方法。
3) 生成靜態代碼塊來初始化接口中方法的Method對象,以及Object類的equals、hashCode、toString方法。
4) 重寫了Object類的equals、hashCode、toString,它們都只是簡單的調用了InvocationHandler的invoke方法,即可以對其進行特殊的操作,也就是說JDK的動態代理還可以代理上述三個方法。
2、cglib動態代理:(具體代碼)
創建被代理的類:
public class UserService { public void delete(){ System.out.println("delete ok!"); } }
測試類:(重點)
//cglib動態代理測試 public class MyTest { @Test /** * cglib動態代理測試 */ public void cglibProxyTest(){ final UserService service=new UserService(); //cglib動態代理\4\ //01.需要類 Enhancer Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(service.getClass()); enhancer.setCallback(new MethodInterceptor(){ public Object intercept(Object obj, Method method, Object[] aobj, MethodProxy methodproxy) throws Throwable { System.out.println("before"); method.invoke(service, aobj); System.out.println("after"); return null; } }); //02.用方法 UserService proxy= (UserService)enhancer.create(); //03. proxy.delete(); } }
實現效果:
二:那些AOP術語
AOP Aspect Oriented Programming 面向切面編程
在軟件業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
面向對象編程是從【靜態角度】考慮程序的結構,而面向切面編程是從【動態角度】考慮程序運行過程。
AOP底層,就是采用【動態代理】模式實現的。采用了兩種代理:JDK動態代理和CGLIB動態代理。
基本術語(一些名詞):
(1)切面(Aspect)
切面泛指[*交叉業務邏輯*]。事務處理和日志處理可以理解為切面。常用的切面有通知(Advice)與顧問(Advisor)。實際就是對主業務邏輯的一種增強。
(2)織入(Weaving)
織入是指將切面代碼插入到目標對象的過程。代理的invoke方法完成的工作,可以稱為織入。
(3) 連接點(JoinPoint)
連接點是指可以被切面織入的方法。通常業務接口的方法均為連接點
(4)切入點(PointCut)
切入點指切面具體織入的方法
注意:被標記為final的方法是不能作為連接點與切入點的。因為最終的是不能被修改的,不能被增強的。
(5)目標對象(Target)
目標對象指將要被增強的對象。即包含主業務邏輯的類的對象。
(6)通知(Advice)
通知是切面的一種實現,可以完成簡單的織入功能。通知定義了增強代碼切入到目標代碼的時間點,是目標方法執行之前執行,還是執行之后執行等。切入點定義切入的位置,通知定義切入的時間。
(7)顧問(Advisor)
顧問是切面的另一種實現,能夠將通知以更為復雜的方式織入到目標對象中,是將通知包裝為更復雜切面的裝配器。
AOP代理 – 由AOP框架生成java對象。
AOP代理方法 = advice + 目標對象的方法。
下面的圖簡化和形象的說明了AOP