两种动态代理(可以看到代理类的样子,方便理解)

这里写目录标题

  • 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的思想以后,再去看源码,发现都离不开动态代理,那就是离不开回调反射

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/697365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【结合OpenAI官方文档】解决Chatgpt的API接口请求速率限制

OpenAI API接口请求速率限制 速率限制以五种方式衡量&#xff1a;RPM&#xff08;每分钟请求数&#xff09;、RPD&#xff08;每天请求数&#xff09;、TPM&#xff08;每分钟令牌数&#xff09;、TPD&#xff08;每天令牌数&#xff09;和IPM&#xff08;每分钟图像数&#x…

BUUCTF第二十四、二十五题解题思路

目录 第二十四题CrackRTF 第二十五题[2019红帽杯]easyRE1 第二十四题CrackRTF 查壳 无壳&#xff0c;32位&#xff0c;用32位IDA打开&#xff0c;打开后的main函数很短&#xff0c;可以找到一句“jmz _main_0”——跳转到 _main_0&#xff0c;说明真正的主函数是_main_0&am…

React 模态框的设计(二)

自定义组件是每个前端开发者必备的技能。我们在使用现有框架时难免有一些超乎框架以处的特别的需求&#xff0c;比如关于弹窗&#xff0c;每个应用都会用到&#xff0c;但是有时我们使用的框架中提供的弹窗功能也是功能有限&#xff0c;无法满足我们的应用需求&#xff0c;今天…

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框 问题: 由于el-select组件的el-select-dropdown部分是自动插入在最外层Body上的&#xff0c;所以在当前组件的scoped中让el-select-dropdown组件display:none不会生效所以需要&#xff1a; :popper-…

TiDB 7.5.0 LTS 高性能数据批处理方案

过去&#xff0c;TiDB 由于不支持存储过程、大事务的使用也存在一些限制&#xff0c;使得在 TiDB 上进行一些复杂的数据批量处理变得比较复杂。 TiDB 在面向这种超大规模数据的批处理场景&#xff0c;其能力也一直在演进&#xff0c;其复杂度也变得越来越低&#xff1a; ○ 从…

11.CSS3的媒介(media)查询

CSS3 的媒介(media)查询 经典真题 如何使用媒体查询实现视口宽度大于 320px 小于 640px 时 div 元素宽度变成 30% 媒体查询 媒体查询英文全称 Media Query&#xff0c;顾名思义就是会查询用户所使用的媒体或者媒介。 在现在&#xff0c;网页的浏览终端是越来越多了。用户可…

C++:string类

标准库中的string类 string类 1. 字符串是表示字符序列的类 2. 标准的字符串类提供了对此类对象的支持&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但添加了专门用于操作单字节字符字符串的设计特性。 3. string类是使用char(即作为它的字符类型&#xff0c;使用…

ChatGPT 是什么

文章目录 一、ChatGPT 是什么二、ChatGPT的发明者三、ChatGPT的运作方式四、ChatGPT的技术五、ChatGPT的优势六、ChatGPT的局限性七、ChatGPT的应用八、ChatGPT的未来九、总结 一、ChatGPT 是什么 OpenAI的ChatGPT&#xff0c;即Chat Generative Pre-Trained Transformer&…

3个精美的wordpress企业网站模板

WordPress企业网站模板 https://www.zhanyes.com/qiye/6305.html WordPress企业官网模板 https://www.zhanyes.com/qiye/6309.html WordPress律师模板 https://www.zhanyes.com/qiye/23.html

SQL注入漏洞解析--less-2

首先我们进入第二关 思路&#xff1a; 1.先判断是什么类型的注入 2.根据类型我们在找注入点 步骤&#xff1a; 1.提示我们输入id数字&#xff0c;那我们先输入1猜一下 2.这里正常回显&#xff0c;当我们后边加上时可以看到报错&#xff0c;且报错信息看不到数字&#xff0…

轻松掌握opencv的8种图像变换

文章目录 opencv的8种图像变换1. 图像放大、缩小2. 图像平移3. 图像旋转4. 图像仿射变换5. 图像裁剪6. 图像的位运算&#xff08;AND, OR, XOR&#xff09;7. 图像的分离和融合8. 图像的颜色空间 opencv的8种图像变换 1. 图像放大、缩小 我们先看下原图 import cv2 import ma…

基于java+springboot+vue实现的美食信息推荐系统(文末源码+Lw)23-170

1 摘 要 使用旧方法对美食信息推荐系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在美食信息推荐系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发…

Shell好用的工具: cut

目标 使用cut可以切割提取指定列\字符\字节的数据 介绍 cut 译为“剪切, 切割” , 是一个强大文本处理工具&#xff0c;它可以将文本按列进行划分的文本处理。cut命令逐行读入文本&#xff0c;然后按列划分字段并进行提取、输出等操作。 语法 cut [options] filename opti…

树中枝繁叶茂:探索 B+ 树、B 树、二叉树、红黑树和跳表的世界

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 树中枝繁叶茂&#xff1a;探索 B 树、B 树、二叉树、红黑树和跳表的世界 前言B树和B树B树&#xff08;Binary Tree&#xff09;&#xff1a;B树&#xff08;B Plus Tree&#xff09;&#xff1a;应用场…

STM32Cubemx TB6612直流电机驱动

一、TB6612FNG TB6612是一个支持双电机的驱动模块&#xff0c;支持PWM调速。PWMA、AIN1、AIN2 为一组控制引脚&#xff0c;PWMA 为 PWM 速度控制引脚&#xff0c;AIN1、AIN2 为方向控制引脚&#xff1b;PWMB、BIN1、BIN2 为一组控制引脚&#xff0c;PWMB 为 PWM 速度控制引脚&…

【力扣hot100】刷题笔记Day11

前言 科研不顺啊......又不想搞了&#xff0c;随便弄弄吧&#xff0c;多花点时间刷题&#xff0c;今天开启二叉树&#xff01; 94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 递归 # 最简单递归 class Solution:def inorderTraversal(self, root: TreeNode) …

idea运行项目时右下角弹出“Lombok requires enabled annotation processing”

文章目录 错误描述原因分析解决方式参考 错误描述 Lombok requires enabled annotation processing&#xff1a;翻译过来就是Lombok 需要启用注释处理 原因分析 idea安装了Lombok插件&#xff0c;但有些设置未做。 解决方式 参考 idea配置和使用Lombok

【FPGA】高云FPGA之数字钟实验->HC595驱动数码管

高云FPGA之IP核的使用 1、设计定义2、设计输入2.1 数码管译码显示2.2 74HC595驱动2.3 主模块设计 3、分析和综合4、功能仿真6.1 hex8模块仿真6.2 HC595模块 5、布局布线6、时序仿真7、IO分配以及配置文件&#xff08;bit流文件&#xff09;的生成8、配置&#xff08;烧录&#…

代码检测规范和git提交规范

摘要&#xff1a;之前开发的项目&#xff0c;代码检测和提交规范都是已经配置好的&#xff0c;最近自己新建的项目就记录下相关配置过程。 1. ESlint配置 2013年6月创建开源项目&#xff0c;提供一个插件化的JavaScript代码检测工具&#xff0c;创建项目是生成的eslintrc.js文…

【算法分析与设计】

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位…