SpringAop實現原理及代理模式
- 2022 年 3 月 31 日
- 筆記
- spring
Spring Aop的原理
Spring的AOP就是通過動態代理實現的。當為某個Bean或者某些Bean配置切面時,Spring會為其創建代理對象,當調用該對象的某個方法時,實際是調用生成的代理類的對象方法。Spring的Aop主要是使用了兩個動態代理,分別是JDK的動態代理和CGLIB動態代理。
1. JDK動態代理
如果代理類實現了介面,Spring默認會使用JDK動態代理。JDK的動態代理是基於反射實現。JDK通過反射,生成一個代理類,這個代理類實現了原來那個類的全部介面,並對介面中定義的所有方法進行了代理。當我們通過代理對象執行原來那個類的方法時,代理類底層會通過反射機制,調用我們實現的InvocationHandler介面的invoke方法。
點擊查看程式碼
/*
* 介面類
*/
public interface Person {
void say();
}
/*
* 介面實現類
*/
public class Man implements Person {
private String word;
public Man(String word){
this.word = word;
}
public Man(){
}
public void say(){
System.out.println("Man Can Say " + word);
}
}
public class ManJDKProxy implements InvocationHandler {
/**
* 需要的代理對象
*/
private Object o;
public Object bind(Object o){
this.o = o;
return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK Proxy Design");
return method.invoke(o, args);
}
}
/**
* JDK動態代理
*/
public class ProxyDesign_2 {
public static void main(String[] args) {
Man man = new Man("Hello");
Person p = (Person)new ManJDKProxy().bind(man);
p.say();
}
}
* JDK動態代理的優缺點
優點:
1. JDK動態代理是JDK原生的,不需要任何依賴即可使用
2. 通過反射機制生成代理類的速度要比CGLib操作位元組碼生成代理類的速度更快
缺點:
1. 如果要使用JDK動態代理,被代理的類必須實現了介面,否則無法代理(InvocationHandler)
2. JDK動態代理無法為沒有在介面中定義的方法實現代理
3. JDK動態代理執行代理方法時,需要通過反射機制進行回調,此時方法執行的效率比較低
2. CGLIB動態代理
若需要代理的類沒有實現介面,JDK的動態代理就無法使用,Spring會使用CGLiB動態代理來生成代理對象。CGLiB直接操作位元組碼,生成類的子類,重寫類的方法完成代理。
點擊查看程式碼
/*
* 介面類
*/
public interface Person {
void say();
}
/*
* 介面實現類
*/
public class Man implements Person {
private String word;
public Man(String word){
this.word = word;
}
public Man(){
}
public void say(){
System.out.println("Man Can Say " + word);
}
}
public class ManCGLIBProxy {
public Object bind(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB Proxy Design");
return method.invoke(target, objects);
}
});
return enhancer.create();
}
}
/**
* CGLIB動態代理
*/
public class ProxyDesign_3 {
public static void main(String[] args) {
Man man = new Man("Hello");
Person p = (Person)new ManCGLIBProxy().bind(man);
p.say();
}
}
* CGLiB動態代理的優缺點
優點:
1. 使用CGLiB代理的類,不需要實現介面,因為CGLib生成的代理類是直接繼承自需要被代理的類
2. 因為CGLiB實現方式是重寫父類的方法,所以對final方法,或者private方法是沒有辦法代理的
3. CGLiB是通過修改位元組碼生成的代理類,所以CGLib執行代理方法的效率要高於JDK的動態代理
缺點:
1. 因為CGLiB實現方式是重寫父類的方法,所以對final方法,或者private方法是沒有辦法代理的
2. 因為CGLiB生成代理類的方式是通過操作位元組碼(asm工具包),這種生成的代理類的方式比JDK通過反射生成代理類的方式的效率低
3. Spring項目中如何強制使用CGLIB代理方式
* xml方式
<!-- aop:config用來在xml中配置切面,指定proxy-target-class="true" -->
<aop:config proxy-target-class="true">
<!-- AOP相關配置 -->
</aop:config>
* @Aspect註解方式
<!-- 將proxy-target-class配置設置為true -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
* 配置類註解方式
添加@EnableAspectJAutoProxy(proxyTargetClass = true)