這里寫目錄標題
- jdk動態代理例子
- CGlib動態代理例子
- 手寫spring中的事務
- 部分自定義注解版aop實現方式
Spring的兩大重點,IOC和AOP,今天我們就來學AOP,眾所周知AOP的底層是動態代理,讓我們看一下這兩種動態代理的區別。
例子:
我們常常使用aop切面編程打印日志,但是他的底層是什么呢?我們常常看到的是封裝好的注解,并不知道他的底層是如何實現的。
那我們下邊先看手動調用動態代理實現執行方法前后打印日志。
jdk動態代理例子
客戶端首先調用代理對象的方法
CalculatorProxy類
package AOP.Proxy;import AOP.service.Calculator;
import AOP.util.Util;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;/*** @BelongsProject: JAVAtest* @BelongsPackage: AOP.Proxy* @Author: GuoYuan.Zhao* @Description: 描述什么人干什么事兒* @CreateTime: 2024-01-24 14:47* @Version: 1.0*/public class CalculatorProxy {//必須要有接口,如果沒有接口,不能使用,這種方式是用jdk提供的reflect 包下邊的類,但是//生產環境中我不能保證每個類都有具體的接口,所有有第二中方式cglib//兩種動態代理的方式,一種是JDK 一種是cglibpublic static Calculator getCalculator( final Calculator calculator){//獲取被代理對象的類加載器ClassLoader loader = calculator.getClass().getClassLoader();Class<?>[] interfaces = calculator.getClass().getInterfaces();InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try{System.out.println(method.getName()+"方法開始執行,參數列表是:"+ Arrays.asList(args));result = method.invoke(calculator,args);System.out.println(method.getName()+"方法結束執行,參數列表是:"+ result);}catch (Exception e){System.out.println(method.getName()+"方法拋出異常"+e.getMessage());}finally {System.out.println(method.getName()+"方法執行結束over");}
//return result;}};Object instance = Proxy.newProxyInstance(loader, interfaces, handler);return (Calculator) instance;}Calculator接口// //獲取被代理對象的類加載器
// ClassLoader loader = calculator.getClass().getClassLoader();
//
// Class<?>[] interfaces = calculator.getClass().getInterfaces();
// InvocationHandler handler = new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object result = null;
// try{System.out.println(method.getName()+"方法開始執行,參數列表是:"+ Arrays.asList(args));
// Util.start(method,args);
// result = method.invoke(calculator,args);System.out.println(method.getName()+"方法開始執行,參數列表是:"+ result);
// Util.stop(method,result);
// }catch (Exception e){System.out.println(method.getName()+"方法拋出異常"+e.getMessage());
// Util.logExpection(method,e);
// }finally {System.out.println(method.getName()+"方法執行結束over");
//
// Util.logFinally(method);
// }// return result;
// }
// };
// Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
// return (Calculator) instance;
// }MyCalculator類// //獲取被代理對象的類加載器
// ClassLoader loader = calculator.getClass().getClassLoader();
//
// Class<?>[] interfaces = calculator.getClass().getInterfaces();
// InvocationHandler handler = new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object result = null;
// try{System.out.println(method.getName()+"方法開始執行,參數列表是:"+ Arrays.asList(args));
// Util.start(method,args);
// result = method.invoke(calculator,args);System.out.println(method.getName()+"方法開始執行,參數列表是:"+ result);
// Util.stop(method,result);
// }catch (Exception e){System.out.println(method.getName()+"方法拋出異常"+e.getMessage());
// Util.logExpection(method,e);
// }finally {System.out.println(method.getName()+"方法執行結束over");
//
// Util.logFinally(method);
// }// return result;
// }
// };
// Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
// return (Calculator) instance;}
public interface Calculator {public Integer add(Integer i,Integer j) throws NoSuchMethodException;public Integer div(Integer i,Integer j);public Integer mul(Integer i,Integer j);public Integer sub(Integer i,Integer j);}
package AOP.service;import AOP.util.Util;
import org.springframework.stereotype.Service;import java.lang.reflect.Method;/*** @BelongsProject: JAVAtest* @BelongsPackage: AOP.service* @Author: GuoYuan.Zhao* @Description: 描述什么人干什么事兒* @CreateTime: 2024-01-24 14:15* @Version: 1.0*/@Service
public class MyCalculator implements Calculator{@Overridepublic Integer add(Integer i, Integer j) throws NoSuchMethodException {//11111
// System.out.println("add方法開始執行:參數是"+i+"___"+j);
// int result = i+j;
// System.out.println("add方法結束,執行結果是"+result);
// return result;//22222// Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
// Util.start("add",i,j);
// Integer result = i+j;
// Util.stop(add,result);
// return result;//333333Integer result = i+j;return result;}@Overridepublic Integer div(Integer i, Integer j) {System.out.println("div方法開始執行:參數是"+i+"___"+j);Integer result = i/j;System.out.println("div方法結束,執行結果是"+result);return result;}@Overridepublic Integer mul(Integer i, Integer j) {System.out.println("mul方法開始執行:參數是"+i+"___"+j);int result = i*j;System.out.println("mul方法結束,執行結果是"+result);return result;}@Overridepublic Integer sub(Integer i, Integer j) {System.out.println("sub方法開始執行:參數是"+i+"___"+j);Integer result = i-j;System.out.println("sub方法結束,執行結果是"+result);return result;}
}
客戶端
主函數{Calculator calculator = CalculatorProxy.getCalculator(new MyCalculator());calculator.add(1,1);saveProxyClass("E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");}public static void saveProxyClass(String path) throws IOException {byte[] $proxy1s = ProxyGenerator.generateProxyClass("$Proxy1", MyCalculator.class.getInterfaces());FileOutputStream out = new FileOutputStream(new File(path + "$Proxy1.class"));out.write($proxy1s);}
最后可以看到代理類
這個類里邊有 我的目標方法,其實客戶端調用的就是這個方法
然后h就是我們上邊那個匿名內部類的對象
CGlib動態代理例子
CglibFactory類
public class CglibFactory implements MethodInterceptor {public CglibFactory(MyCalculatorCGlib target) {}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("執行方法"+ method.getName());Object result = methodProxy.invokeSuper(o, objects);//這里實現將返回值字符串變為大寫的邏輯
// if(result != null) {
// result = ((String) result).toUpperCase();
// }System.out.println("執行方法完畢結果是"+result);return result;}public MyCalculatorCGlib myCglibCreator() {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");Enhancer enhancer = new Enhancer();//將目標類設置為父類,cglib動態代理增強的原理就是子類增強父類,cglib不能增強目標類為final的類//因為final類不能有子類enhancer.setSuperclass(MyCalculatorCGlib.class);//設置回調接口,這里的MethodInterceptor實現類回調接口,而我們又實現了MethodInterceptor,其實//這里的回調接口就是本類對象,調用的方法其實就是intercept()方法enhancer.setCallback(this);//create()方法用于創建cglib動態代理對象return (MyCalculatorCGlib) enhancer.create();}
}
MyCalculatorCGlib類
public class MyCalculatorCGlib {public Integer add(int i, int j) {Integer result = i+j;return result;}public void doSecond() {
// System.out.println("doSecond()方法");}}
客戶端
MyCalculatorCGlib target = new MyCalculatorCGlib();
// System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");MyCalculatorCGlib proxy = new CglibFactory(target).myCglibCreator();Integer result = proxy.add(1,1);
按照上邊的思路,說完上邊兩種代理方式,我們下邊來說如何使用這種方式手寫事務呢
手寫spring中的事務
首先從原理分析,事務的原理就是當sql出現問題的時候,數據回滾為原來的樣子,具體他們是通過spring中的DataSourceTransactionManager實現的,在sql執行前,開啟事務事務,然后執行sql,如果正常就提交事務,如果不正常就回滾事務。
這就是大概得邏輯,是不是就像上邊打印日志一樣,在執行方法前進行一些操作,在執行方法后進行一些操作。
public class Main {public static void main(String[] args) {
// TransactionManager transactionManager = new SimpleTransactionManager();
// TransactionProxyFactory factory = new TransactionProxyFactory(transactionManager);
// MyService service = factory.createProxy(new MyService());
// service.doSomething(); // 這個調用將被代理攔截,并處理事務MyService myService = new MyServiceImpl();TransactionManager transactionManager = new SimpleTransactionManager();DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();HikariDataSource dataSource = new HikariDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai");dataSource.setUsername("root");dataSource.setPassword("123456");dataSourceTransactionManager.setDataSource(dataSource);transactionManager.setDataSourceTransactionManager(dataSourceTransactionManager);// 創建代理對象MyService proxy = TransactionalInvocationHandler.createProxy(myService, transactionManager, MyService.class);// 調用代理對象的方法,這將觸發事務管理proxy.doSomething();}
}
@Service
public interface MyService {// @Transactionalpublic void doSomething() ;
}
@Service
public class MyServiceImpl implements MyService {@Overridepublic void doSomething() {try {System.out.println("我真的要執行sql語句");int i = 1/0;} catch (ArithmeticException e) {// 處理算術異常,例如記錄日志或返回錯誤消息throw new RuntimeException("An arithmetic operation has failed", e);}}
}
@Component
public interface TransactionManager {TransactionStatus beginTransaction();void commitTransaction(TransactionStatus transactionStatus);void rollbackTransaction(TransactionStatus transactionStatus);public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) ;}
@Component
public class SimpleTransactionManager implements TransactionManager {private boolean isActive = false;@Overridepublic void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) {this.dataSourceTransactionManager = dataSourceTransactionManager;}private DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();@Overridepublic TransactionStatus beginTransaction() {if (isActive) {throw new IllegalStateException("Transaction already active");}// 模擬事務開始邏輯(在實際應用中,這里會涉及到數據庫連接的獲取、設置隔離級別等操作)TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());isActive = true;System.out.println("開啟事務了");return transaction;}@Overridepublic void commitTransaction(TransactionStatus transactionStatus) {if (!isActive) {throw new IllegalStateException("No active transaction");}// 模擬事務提交邏輯dataSourceTransactionManager.commit(transactionStatus);isActive = false;System.out.println("提交事務了");}@Overridepublic void rollbackTransaction(TransactionStatus transactionStatus) {if (!isActive) {throw new IllegalStateException("No active transaction");}// 模擬事務回滾邏輯dataSourceTransactionManager.rollback(transactionStatus);isActive = false;System.out.println("回滾事務了");}
}
public class TransactionalInvocationHandler implements InvocationHandler {@Autowiredprivate final Object target;@Autowiredprivate final TransactionManager transactionManager;public TransactionalInvocationHandler(Object target, TransactionManager transactionManager) {this.target = target;this.transactionManager = transactionManager;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Exception {TransactionStatus transactionStatus = null;try {transactionStatus = transactionManager.beginTransaction();Object result = method.invoke(target, args);transactionManager.commitTransaction(transactionStatus);return result;} catch (Exception e) {transactionManager.rollbackTransaction(transactionStatus);throw e;}}public static <T> T createProxy(T target, TransactionManager transactionManager, Class<T> interfaceType) {return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),new Class<?>[]{interfaceType},new TransactionalInvocationHandler(target, transactionManager));}
}
運行結果:
部分自定義注解版aop實現方式
自定義注解 MyTransactionAnnotation
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactionAnnotation {
}
MyTransaction類
@Component
@Slf4j
public class MyTransaction {@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;/*** 開啟事務,并配置默認的事務傳播機制* @return*/public TransactionStatus begin(){TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());log.info("事務開啟成功");return transaction;}/*** 事務提交* @param transaction*/public void commit(TransactionStatus transaction){log.info("事務提交成功");dataSourceTransactionManager.commit(transaction);}/*** 事務回滾* @param transaction*/public void rollback(TransactionStatus transaction){log.info("事務回滾成功");dataSourceTransactionManager.rollback(transaction);}
}
MyTransactionAop類
@Slf4j
@Aspect
@Component
public class MyTransactionAop {@Autowiredprivate MyTransaction myTransaction;@Around(value = "@annotation(com.transaction.annotation.MyTransactionAnnotation)")public Object myTransactionAop(ProceedingJoinPoint joinPoint){log.info("進入到事務aop, 準備開啟事務");TransactionStatus transactionStatus = myTransaction.begin();try {log.info("準備執行目標方法");// 執行目標方法Object object = joinPoint.proceed();log.info("目標方法執行完畢,準備提交");// 執行方法完畢,提交事務,并返回myTransaction.commit(transactionStatus);return object;}catch (Throwable throwable) {log.error("目標函數異常,手動回滾");// 目標函數異常,手動回滾myTransaction.rollback(transactionStatus);return "目標函數異常";}}
}
TestController類
@RestController
@Slf4j
public class TestController {@GetMapping("test_transaction")@MyTransactionAnnotationpublic String testTransaction(@RequestParam(value = "name") String name){// int i = 1/0;return name;}
}
@SpringBootApplication
@EnableTransactionManagement
public class HbzTransactionApplication {public static void main(String[] args) {SpringApplication.run(HbzTransactionApplication.class, args);}
}
application.yml文件
server:port: 9001servlet:context-path: /test
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456
總結:當我們大概了解這個AOP的思想以后,再去看源碼,發現都離不開動態代理,那就是離不開回調反射