对 spring 中两种不同动态代理技术的思考

待完善:cglib 代理类是什么样子,JDK 动态代理是什么样子

事务类型

通常我们只会用到 @Transactional(propagation = Propagation.REQUIRED)

在特殊需求的时候需要在一个方法内部提前提交一部分事务或者是让内部的一段代码处于单独的一个事务管理的时候需要用到 REQUIRES_NEW

基于继承的代理

cglib 实现动态代理就是基于继承实现的。

public class ProxyReferenceTest {

    @Test
    public void testReferenceRelation() {
        EatProxyClass eatProxyClass = new EatProxyClass();
        eatProxyClass.eat();
    }

    static class EatTargetClass  {
        public void eat(){
            System.out.println("EatTargetClass 中 this -->" + this);
        }
    }

    static class EatProxyClass extends EatTargetClass {
        public void eat(){
            System.out.println("EatProxyClass 中 this -->" + this);
            super.eat();
        }
    }
}

测试方法运行结果如下
在这里插入图片描述
通过上述测试我们可以得出结论:基于继承的代理是目标对象和代理对象的引用是一样。

基于接口的代理

jdk 实现动态代理就是基于接口实现的。

public class ProxyReferenceTest {

    @Test
    public void testReferenceRelation() {
        EatTargetClass eatTargetClass = new EatTargetClass();
        EatProxyClass eatProxyClass = new EatProxyClass(eatTargetClass);
        eatProxyClass.eat();
    }

    static interface EatService {
        void eat();
    }

    static class EatTargetClass implements EatService{
        @Override
        public void eat(){
            System.out.println("TargetClass 中 this-->" + this);
        }
    }

    static class EatProxyClass implements  EatService{
        EatTargetClass eatTargetClass;

        public EatProxyClass(EatTargetClass eatTargetClass){
            this.eatTargetClass = eatTargetClass;
        }

        @Override
        public void eat() {
            System.out.println("EatProxyClass 中 this -->" + this);
            eatTargetClass.eat();
        }
    }
}

测试方法运行结果如下
在这里插入图片描述
通过上述测试我们可以得出结论:基于接口的代理是目标对象和代理对象的引用是不一样。

spring 源码中动态代理的思考

我们都知道 spring 的 AOP 是通过动态代理实现的,动态代理的实现技术又分为:

  1. cglib 动态代理
  2. JDK 动态代理

spring 中的声明式事务也是用 AOP 实现的,通过生成代理对象来达到一个事务的效果。我们经常遇到一个事务方法(这里为了方便后续描述,假定这个方法为 saveOrder)中调度另一个事务方法(假定这个方法为 updateStock)时,updateStock 这个方法的事务不起作用。产生这个问题的原因在于我们在 saveOrder 方法中的 this 对象引用是目标对象的引用,不是事务代理对象的引用,自然也就不会被事务代理对象进行逻辑的增强。

而如果使用 cglib 这种动态代理的技术去生成事务代理对象,saveOrder 方法中的 this 对象引用就是事务代理对象的引用,从而调度另一个事务方法 updateStock 时,updateStock 是可以起到事务特性作用的。

为了大家更好理解我上诉说的意思,我再拿 spring 中 @Configuration 这个例子来讲述。

加了 @Configuration 注解的类是会被 ConfigurationClassPostProcessor 这个后置处理器处理的。

@Configuration
public class AppConfig {
    @Bean
    public Student student(){
        return new Student();
    }

    @Bean
    public Teacher teacher(){
        return new Teacher( student() );
    }
}

teacher 这个 bean 方法内部调用了另外一个 bean 方法 student,这里调用 student 这个方法返回的对象是和直接调用 student 方法返回的对象是一致的。

这个是怎么实现呢?
其实就是通过生成代理对象实现的。ConfigurationClassPostProcessor 这个后置处理器会在 spring context 初始化(refresh)时,给加了 @Configuration 注解的类生成 cglib 代理对象。这个代理对象对于执行加了 @Bean 注解的方法执行前做一个拦截,从而保证 @Bean 方法多次调用生成的是同一个对象。

那问题来了,一个 bean 方法内部调用另外一个 bean 方法,为什么能执行到切面呢 ?
其实答案就是在于我们这个代理对象是基于继承生成的,再回顾一下我上面说的那句话:基于继承的代理是目标对象和代理对象的引用是一样。

参考

https://www.jianshu.com/p/3e9267b025b2

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值