代理模式
我們知道,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動態代理
1.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 后置通知
2.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 后置通知
3.兩種代理的區別
JDK動態代理和CgLib動態代理的主要區別:
JDK動態代理只能針對實現了接口的類的接口方法進行代理
CgLib動態代理基於繼承來實現代理,所以無法對final類、private方法和static方法實現代理
Spring AOP的代理
Spring AOP中的代理使用的默認策略是:
如果目標對象實現了接口,則默認采用JDK動態代理
如果目標對象沒有實現接口,則采用CgLib進行動態代理
如果目標對象實現了接口,且強制CgLib代理,則采用CgLib進行動態代理