• JDK动态代理和CGLIB动态代理介绍


    我们先看一下下面这段代码:
    在这里插入图片描述
    可以看到,这里为了增加事务,对业务代码的侵入性较强,不利于后期的维护,那么有没有办法使业务代码和事务代码分开呢?答案肯定是有的,在Spring中可以使用AOP进行解耦,但是其底层其实是使用的动态代理实现的,那么我们在这里就介绍一下两种常见的动态代理模式:JDK动态代理CGLIB动态代理

    常用的动态代理技术
    JDK 代理 : 基于接口的动态代理技术·:利用拦截器(必须实现InvocationHandler)加上反射机制生成
    一个代理接口的匿名类,再调用具体方法前调用InvokeHandler来处理,从而实现方法增强

    CGLIB 代理 : 基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是
    final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行
    增强
    在这里插入图片描述
    如上图,目标对象(目标类)就是被代理的对象,

    • JDK动态代理:代理类是去实现目标类的接口(目标类必须实现接口),和目标类同级

    • CGLIB动态代理:代理类是采用ASM生成目标类的子类,和目标类是继承关系

    • Spring在选择用JDK还是CGLib的依据:
      当Bean实现接口时,Spring就会用JDK的动态代理
      当Bean没有实现接口时,Spring使用CGLib来实现
      可以强制使用CGLib(在Spring配置中加入

    性能比较:
    在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点

    实现方式:

    JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
     1. 实现InvocationHandler接口,重写invoke()
     2. 使用 Proxy.newProxyInstance() 产生代理对象
     3. 被代理的对象必须要实现接口

    CGLib 必须依赖于CGLib的类库,需要满足以下要求:
     1. 实现MethodInterceptor接口,重写intercept()
     2. 使用 Enhancer对象.create() 产生代理对象
     3. 被代理的对象不能使用final修饰

    一、JDK动态代理方式

    Jdk工厂类

    /*
        JDK动态代理工厂类
     */
    @Component
    public class JDKProxyFactory {
    
        @Autowired
        private AccountService accountService;
    
        @Autowired
        private TransactionManager transactionManager;
    
        /*
            采用JDK动态代理技术来生成目标类的代理对象
             ClassLoader loader, : 类加载器:借助被代理对象获取到类加载器
             Class[] interfaces, : 被代理类所需要实现的全部接口
             InvocationHandler h : 当代理对象调用接口中的任意方法时,那么都会执行InvocationHandler中invoke方法
         */
        public AccountService createAccountSericeJDKProxy(){
    
            AccountService accountServiceProxy= (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
    
                @Override      
                //   proxy: 当前的代理对象引用   method:被调用的目标方法的引用   
                //   args:被调用的目标方法所用到的参数
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    try {
                            if(method.getName().equals("transfer")) {
                                System.out.println("进行了前置增强");
                                // 手动开启事务:调用事务管理器类中的开启事务方法
                                transactionManager.beginTransaction();
    
                                // 让被代理对象的原方法执行
                                method.invoke(accountService, args);
                                System.out.println("进行了后置增强");
    
                                // 手动提交事务
                                transactionManager.commit();
                            }else {
    
                                method.invoke(accountService, args);
                            }
                    } catch (Exception e) {
                        e.printStackTrace();
                        // 手动回滚事务
                        transactionManager.rollback();
                    } finally {
                        // 手动释放资源
                        transactionManager.release();
                    }
                    
                    return null;
                }
            });
    
            return  accountServiceProxy;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    在这里插入图片描述
    这里, method.invoke(accountService, args) 进行业务方法的调用,然后在前后增加了事务的处理

    测试代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:applicationContext.xml"})
    public class AccountServiceImplTest {
    
    	@Autowired
        private JDKProxyFactory proxyFactory;
    	    /*
            测试JDK动态代理优化转账案例
         */
        @Test
        public void testTransferProxyJDK(){
    
            // 当前返回的实际上是AccountService的代理对象proxy
            AccountService accountSericeJDKProxy = proxyFactory.createAccountSericeJDKProxy();
            // 代理对象proxy调用接口中的任意方法时,都会执行底层的invoke方法
            accountSericeJDKProxy.transfer("tom","jerry",100d);
            accountSericeJDKProxy.save();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    二、CGLIB动态代理方式

    Cglib工厂类

    /*
       该类就是采用cglib动态代理来对目标类(AccountServiceImpl)进行方法(transfer)的动态增强(添加上事务控制)
     */
    @Component
    public class CglibProxyFactory {
    
        @Autowired
        private AccountService accountService;
    
        @Autowired
        private TransactionManager transactionManager;
    
        public AccountService createAccountServiceCglibProxy(){
            // 编写cglib对应的API来生成代理对象进行返回
            // 参数1 : 目标类的字节码对象
            // 参数2:  动作类,当代理对象调用目标对象中原方法时,那么会执行intercept方法
            AccountService accountServiceproxy = (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() {
    
                // o : 代表生成的代理对象   method:调用目标方法的引用  
                // objects:方法入参    methodProxy:代理方法
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
                    try {
                        // 手动开启事务:调用事务管理器类中的开启事务方法
                        transactionManager.beginTransaction();
    
                        method.invoke(accountService, objects);
    
                        transactionManager.commit();
                    } catch (Exception e) {
                        e.printStackTrace();
                        // 手动回滚事务
                        transactionManager.rollback();
                    } finally {
                        // 手动释放资源
                        transactionManager.release();
                    }
    
                    return null;
                }
            });
            return accountServiceproxy;
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    这里, method.invoke(accountService, objects) 进行业务方法的调用,然后在前后增加了事务的处理

    测试代码

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:applicationContext.xml"})
    public class AccountServiceImplTest {
    
    	@Autowired 
    	private CglibProxyFactory cglibProxyFactory;
    	
    	/*
           测试Cglib动态代理优化转账案例
        */
        @Test
        public void testTransferProxyCglib(){
    
            // accountServiceCglibProxy: proxy
            AccountService accountServiceCglibProxy = cglibProxyFactory.createAccountServiceCglibProxy();
            accountServiceCglibProxy.transfer("tom","jerry",100d);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    14款DevOps/SRE工具,助力提升运维效率
    [一篇读懂]C语言八讲:数据结构概述
    [vue]vuex
    “第六十三天”
    【JavaWeb】MVC
    XUI - 一个简洁而优雅的Android原生UI框架
    httpserver 下载服务器demo 以及libevent版本的 httpserver
    每天一个Git 知识
    js中数组的遍历的几种方法
    Danswer 接入 Llama 2 模型 | 免费在 Google Colab 上托管 Llama 2 API
  • 原文地址:https://blog.csdn.net/u012660464/article/details/126590390