Spring AOP實現原理-動態代理
目錄
代理模式
我們知道,Spring AOP的主要作用就是不通過修改原始碼的方式、將非核心功能程式碼織入來實現對方法的增強。那麼Spring AOP的底層如何實現對方法的增強?實現的關鍵在於使用了代理模式
代理模式的作用就是為其它物件提供一種代理,以控制對這個物件的訪問,用於解決在直接訪問物件時帶來的各種問題。
代理主要分為兩種方式:靜態代理和動態代理
靜態代理
靜態代理主要通過將目標類與代理類實現同一個介面,讓代理類持有真實類物件,然後在代理類方法中呼叫真實類方法,在呼叫真實類方法的前後新增我們所需要的功能擴充套件程式碼來達到增強的目的
示例程式碼:
/** * 代理類與目標類的共同介面 */ public interface Subject { void request(); void response(); } /** * 目標類 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("執行目標物件的request方法......"); } @Override public void response() { System.out.println("執行目標物件的response方法......"); } } /** * 代理類 */ public class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("before 前置增強"); subject.request(); System.out.println("after 後置增強"); } @Override public void response() { System.out.println("before 前置增強"); subject.response(); System.out.println("after 後置增強"); } } public class Main { public static void main(String[] args) { //目標物件 Subject realSubject = new RealSubject(); //代理物件 通過構造器注入目標物件 Subject proxySubject = new ProxySubject(realSubject); proxySubject.request(); proxySubject.response(); } }
執行結果:
before 前置增強
執行目標物件的request方法......
after 後置增強
before 前置增強
執行目標物件的response方法......
after 後置增強
通過以上的程式碼示例,我們不難發現靜態代理的缺點。假如我們的Subject介面要增加其它的方法,則ProxySubject代理類也必須同時代理這些新增的方法。同時我們也看到,request方法和response方法所織入的程式碼是一樣的,這會使得代理類中出現大量冗餘的程式碼,非常不利於擴充套件和維護。為了解決靜態代理的這些缺陷,於是有了動態代理
動態代理
與靜態代理相比,動態代理的代理類不需要程式設計師自己手動定義,而是在程式執行時動態生成
動態代理可以分為JDK動態代理和CgLib動態代理
JDK動態代理
JDK動態代理與靜態代理一樣,目標類需要實現一個代理介面,它的開發步驟如下:
1.定義一個java.lang.reflect.InvocationHandler介面的實現類,重寫invoke方法
2.將InvocationHandler物件作為引數傳入java.lang.reflect.Proxy的newProxyInstance方法中
3.通過呼叫java.lang.reflect.Proxy的newProxyInstance方法獲得動態代理物件
4.通過代理物件呼叫目標方法
示例程式碼:
/**
* 自定義InvocationHandler的實現類
*/
public class JdkProxySubject implements InvocationHandler {
private Subject subject;
public JdkProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before 前置通知");
Object result = null;
try {
result = method.invoke(subject, args);
}catch (Exception ex) {
System.out.println("ex: " + ex.getMessage());
throw ex;
}finally {
System.out.println("after 後置通知");
}
return result;
}
}
public class Main {
public static void main(String[] args) {
//獲取InvocationHandler物件 在構造方法中注入目標物件
InvocationHandler handler = new JdkProxySubject(new RealSubject());
//獲取代理類物件
Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);
//呼叫目標方法
proxySubject.request();
proxySubject.response();
}
}
執行結果:
before 前置通知
執行目標物件的request方法......
after 後置通知
before 前置通知
執行目標物件的response方法......
after 後置通知
CgLib動態代理
CgLib動態代理的原理是對指定的業務類生成一個子類,並覆蓋其中的業務方法來實現代理。它的開發步驟:
1.定義一個org.springframework.cglib.proxy.MethodInterceptor介面的實現類,重寫intercept方法
2.獲取org.springframework.cglib.proxy.Enhancer類的物件
3.分別呼叫Enhancer物件的setSuperclass和setCallback方法,使用create方法獲取代理物件
4.通過代理物件呼叫目標方法
示例程式碼:
/**
* 自定義MethodInterceptor實現類
*/
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before 前置通知");
Object result = null;
try {
result = methodProxy.invokeSuper(obj, args);
}catch (Exception ex) {
System.out.println("ex: " + ex.getMessage());
throw ex;
}finally {
System.out.println("after 後置通知");
}
return result;
}
}
public class Main {
public static void main(String[] args) {
//獲取Enhancer 物件
Enhancer enhancer = new Enhancer();
//設定代理類的父類(目標類)
enhancer.setSuperclass(RealSubject.class);
//設定回撥方法
enhancer.setCallback(new MyMethodInterceptor());
//獲取代理物件
Subject proxySubject = (Subject)enhancer.create();
//呼叫目標方法
proxySubject.request();
proxySubject.response();
}
}
before 前置通知
執行目標物件的request方法......
after 後置通知
before 前置通知
執行目標物件的response方法......
after 後置通知
兩種代理的區別
JDK動態代理和CgLib動態代理的主要區別:
JDK動態代理只能針對實現了介面的類的介面方法進行代理
CgLib動態代理基於繼承來實現代理,所以無法對final類、private方法和static方法實現代理
Spring AOP的代理
Spring AOP中的代理使用的預設策略是:
如果目標物件實現了介面,則預設採用JDK動態代理
如果目標物件沒有實現介面,則採用CgLib進行動態代理
如果目標物件實現了介面,且強制CgLib代理,則採用CgLib進行動態代理