SpringAOP中的動態(tài)代理技術(shù)深入解析
動態(tài)代理
Spring的aop實現(xiàn)主要應(yīng)用了JDK動態(tài)代理和Cglib動態(tài)代理 這2種代理。
org.springframework.aop.framework.JdkDynamicAopProxy org.springframework.aop.framework.CglibAopProxy
spring默認使用JDK動態(tài)代理實現(xiàn)AOP,類如果實現(xiàn)了接口,spring就會用JDK動態(tài)代理實現(xiàn)AOP. 如果目標類沒有實現(xiàn)接口,spring則使用Cglib動態(tài)代理來實現(xiàn)AOP.
- jdk動態(tài)代理的優(yōu)勢:jdk自身支持,減少依賴,可隨jdk平滑升級,代碼實現(xiàn)簡單
- cglib動態(tài)代理的優(yōu)勢:無需實現(xiàn)接口,達到無侵入,只操作我們關(guān)系的類,而不必為其他相關(guān)類增加工作量
spring事務(wù)便是基于spring aop實現(xiàn)的, spring事務(wù)的一些失效場景,如
1.使用了private方法,導(dǎo)致事務(wù)失效(被動態(tài)代理的方法必須是public)
2.使用了final方法,導(dǎo)致事務(wù)失效(如果方法定義為final,JDK動態(tài)代理和cglib動態(tài)代理無法重寫該方法)
3.同一類內(nèi)部方法調(diào)用:直接使用this對象調(diào)用方法,無法生成代理方法,導(dǎo)致事務(wù)失效
測試使用Cglib動態(tài)代理
定義被代理的方法
package cn.demo.cglib;
public class OrderService {
/**
* 被動態(tài)代理的方法應(yīng)該是public公共的
* @param orderNo
*/
public void order(String orderNo){
System.out.println("order something...");
}
}定義cglib動態(tài)代理時觸發(fā)的攔截器
package cn.demo.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 動態(tài)代理類,實現(xiàn)方法攔截器MethodInterceptor接口
*/
public class LogInterceptor implements MethodInterceptor {
//cglib動態(tài)代理,基于ASM機制實現(xiàn),通過生成目標類的子類作為代理類
//使用cglib中的Enhancer來生成代理對象子類,
public Object getProxyInstance(Class targetClass){
//1.工具類
Enhancer enhancer = new Enhancer();
//2.設(shè)置父類
enhancer.setSuperclass(targetClass);
//3.設(shè)置回調(diào)函數(shù)
enhancer.setCallback(this);
//4.創(chuàng)建子類(代理對象)
return enhancer.create();
}
//并實現(xiàn)MethodInterceptor的intercept方法來實現(xiàn)增強功能
@Override
public Object intercept(Object o, Method method,
Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("LogInterceptor: Before invoke---");
Object obj = methodProxy.invokeSuper(o,objects);
System.out.println("LogInterceptor: After invoke---");
return obj;
}
}
使用cglib動態(tài)代理出的實例
package cn.demo.cglib;
public class CglibTest {
public static void main(String[] args) {
//Cglib動態(tài)代理特點
//1.需要引入cglib依賴jar (一般spring的核心包已經(jīng)有了cglib依賴)
//2.被代理的類不需要接口信息,Cglib動態(tài)代理可以攔截并包裝被代理類的所有方法
//3.代理類要實現(xiàn)MethodInterceptor接口
//4.代理類使用cglib中的Enhancer來生成目標對象子類
//5.目標類不能為final,因為final類不能創(chuàng)建子類
OrderService orderService = (OrderService)
new LogInterceptor()
.getProxyInstance(OrderService.class);
orderService.order("123");
}
}測試使用jdk動態(tài)代理
定義被代理的方法及其接口
package cn.demo.proxy;
public interface UserService {
void login(String username, String password);
}
package cn.demo.proxy;
//jdk動態(tài)代理的致命缺點就是目標類必須實現(xiàn)某個接口
public class UserServiceImpl implements UserService{
@Override
public void login(String username, String password) {
System.out.println("User Login Service!");
}
}定義jdk動態(tài)代理時觸發(fā)的攔截器
package cn.demo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//通過實現(xiàn)InvocationHandler接口完成代理邏輯
public class LogHandler implements InvocationHandler {
/**
* 被代理的對象,實際的方法執(zhí)行者
*/
Object target;
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在函數(shù)invoke之前之后,可以進行一些自定義操作
//如 日志、事務(wù)、攔截器、權(quán)限控制等
System.out.println("Before Login ---");
Object result = method.invoke(target,args);
System.out.println("After Login ---");
return result;
}
}使用jdk動態(tài)代理出的實例
package cn.demo.proxy;
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
//創(chuàng)建被代理的對象, UserService接口的實現(xiàn)類
UserServiceImpl userService = new UserServiceImpl();
//創(chuàng)建代理對象,包含3個參數(shù) ClassLoader 、 目標類實現(xiàn)接口數(shù)組、 事件處理器
UserService userProxy = (UserService)
Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new LogHandler(userService)
);
//用代理出的實例調(diào)用login方法
userProxy.login("admin", "123456");
}到此這篇關(guān)于SpringAOP中的動態(tài)代理技術(shù)深入解析的文章就介紹到這了,更多相關(guān)SpringAOP動態(tài)代理技術(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jdk環(huán)境變量配置切換jdk版本及安裝jdk后環(huán)境變量不生效問題解決辦法
這篇文章主要介紹了jdk環(huán)境變量配置切換jdk版本及安裝jdk后環(huán)境變量不生效問題解決辦法,包括配置JAVA_HOME、Path和CLASSPATH,以及如何驗證配置是否成功,文章還講解了如何切換JDK版本,并解決了安裝新JDK后環(huán)境變量配置不生效的問題,需要的朋友可以參考下2024-12-12
SpringBoot Filter修改返回內(nèi)容,解決請求卡死200的錯誤
這篇文章主要介紹了SpringBoot Filter修改返回內(nèi)容,解決請求卡死200的錯誤問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
JAVA中l(wèi)ist,set,數(shù)組之間的轉(zhuǎn)換詳解
以下是對JAVA中l(wèi)ist,set,數(shù)組之間的轉(zhuǎn)換進行了詳細的分析介紹,需要的朋友可以過來參考下2013-09-09
SpringCloud中分析講解Feign組件添加請求頭有哪些坑梳理
在spring?cloud的項目中用到了feign組件,簡單配置過后即可完成請求的調(diào)用。又因為有向請求添加Header頭的需求,查閱了官方示例后,就覺得很簡單,然后一頓操作之后調(diào)試報錯...下面我們來詳細了解2022-06-06
在mybatis執(zhí)行SQL語句之前進行攔擊處理實例
本篇文章主要介紹了在mybatis執(zhí)行SQL語句之前進行攔擊處理實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04

