Spring Boot動態(tài)代理分兩種,JDK的動態(tài)代理和cglib的動態(tài)代理
Spring Boot默認使用JDK的動態(tài)代理,當類沒有實現(xiàn)接口時才使用cglib的動態(tài)代理。
- jdk的動態(tài)代理
這種代理適用于實現(xiàn)了接口的類
假設(shè)現(xiàn)在有一個接口,里面有很多抽象方法
public interface UserService {
public void method1();
public void method2();
public void method3();
public void method4();
}
有一個類
//將被代理的類??匆钥吹?,這個類實現(xiàn)了UserService接口
public class UserImpl implements UserService {
public void query(String name) {
System.out.println("query name = " + name);
}
}
那怎么代理上面的這個類呢,JDK動態(tài)代理的思想是生成一個類,讓它和被代理對象實現(xiàn)同樣的接口,并重寫接口的方法。如下所示。(注意:下面這個類是JDK自動生成的)
//它與被代理類實現(xiàn)了相同的接口,即UserService
public class LogUserProxy implements UserService {
//被代理對象將作為生成類的一個屬性
private UserService userService;
//構(gòu)造方法將被代理對象注入進去
public LogUserProxy(UserService userService) {
this.userService = userService;
}
//重寫接口的方法,實現(xiàn)代理原方法的功能
public void query(String name) {
//todo 增強邏輯
System.out.println("增強邏輯");
userService.query(name);
//todo 增強邏輯
System.out.println("增強邏輯");
}
}
上面有兩個類,UserImpl(被代理的類)和LogUserProxy(自動生成的類)
面向接口編程中常常使用如下形式調(diào)用UserImpl(Spring Boot中會自動注入)
UserService service = new UserImpl();
service.query("aaa");
如果這個時候我們將上述代碼改為如下形式即可完成代理
UserService service = new LogUserProxy();
service.query("aaa");
2.cglib動態(tài)代理
jdk的動態(tài)代理只能代理實現(xiàn)了接口的類,但是有些類沒有實現(xiàn)接口,那這種類如何被代理呢?
例如被代理類如下所示。
//被代理類
public class Test {
public void cal() {
System.out.println("被代理方法");
}
}
代理方式:
自定義攔截器并實現(xiàn)MethodInterceptor 接口
public class AIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增強邏輯
System.out.println("代理前,增強邏輯");
methodProxy.invokeSuper(o, objects);
//增強邏輯
System.out.println("代理后,增強邏輯");
return null;
}
}
測試:
public class Main1 {
public static void main(String[] args) {
//代理類class文件存入本地磁盤,方便查看中間生成的臨時文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
//------------------開始代理--------------------
Enhancer enhancer = new Enhancer();
//指定被代理類,生成的類將會繼承此類
enhancer.setSuperclass(Test.class);
//指定攔截器
enhancer.setCallback(new AIntercepter());
//生成代理類
Test t = (Test)enhancer.create();
//完成代理
t.cal();
//------------------完成代理--------------------
}
}
enhancer.create()方法會生成代理類,而System.setProperty()會將其保存到D:\code目錄下。查看目錄,會發(fā)現(xiàn)D:\code下生成了三個文件
Test$$EnhancerByCGLIB$$7ce11e59$$FastClassByCGLIB$$fe330774.class
Test$$EnhancerByCGLIB$$7ce11e59.class
Test$$FastClassByCGLIB$$3b8fb982.class
其中第二個文件是代理類,將其反編譯,將其中重要的方法展示如下。
//攔截其中的methodProxy.invokeSuper(o, objects);會調(diào)用此方法
final void CGLIB$cal$0() {
super.cal();
}
//測試代碼中的t.cal()會調(diào)用此方法
public final void cal() {
//獲取自定義攔截器
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//調(diào)用自定義攔截器的intercept方法
var10000.intercept(this, CGLIB$cal$0$Method, CGLIB$emptyArgs, CGLIB$cal$0$Proxy);
} else {
super.cal();
}
}
整個過程的方法調(diào)用邏輯是
Main1中的t.cal()---->Test$$EnhancerByCGLIB$$7ce11e59.class中的cal()方法----->自定義攔截器AIntercepter中的intercept()方法----->被代理類Test中的cal()方法
其中自定義攔截器中實現(xiàn)方法增強