Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别

Spring Aop 机制可适用于 日志,权限,事务等应用场景,Aop的使用及注意事项在之前的博客有写过https://blog.csdn.net/qq_32140607/article/details/97395223

今天说说Aop生成代理对象的过程中与代理的关系

先说结论:

1:spring 的Aop通过动态代理来实现,代理分为jdk动态代理与cglib代理两部分

2:jdk动态代理的对象通过继承Poxy对象并实现被代理对象的接口来完成代理(重点)

3:cglib动态代理通过继承被代理对象来完成代理

4:注解@EnableAspectJAutoProxy(proxyTargetClass = false)中proxyTargetClass属性默认为false,该属性的值将影响aop生成动态代理时的选择

spring中 AopProxy 关系如图

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第1张图片

接下来看看举例,依旧放上代码:


// 一个普通接口
public interface TestAopService {
    void showTxt();
}

// 实现了上述接口
@Service
public class TestAopServiceImpl implements TestAopService {
    @Override
    public void showTxt() {
        System.out.println("testAopServiceImpl.showTxt");
    }
}

//一个普通类,未实现任何接口
@Component
public class TestAop {
    public void showTxt(){
        System.out.println("TestAop.showTxt");
    }
}

// aop的切点与增强配置
@Aspect
@Component
public class Aop {

    @Pointcut("execution(public *  demo.AOP.TestAop.*(..))")
    public void showId(){}

    @Before("showId()")
    public void doBefore(){
        System.out.println("------------execution(public *  demo.AOP.TestAop.*(..))------------");
    }

    @Before("this(demo.AOP.TestAopServiceImpl)")
    public void doBefore2(){
        System.out.println("------------this(demo.AOP.TestAopServiceImpl)------------");
    }
}


//用来测试的main方法 被注释的地方解开会报错
public class AopMain {
    public static void main(String[] args){
        AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(SpringConfig.class);
        TestAop taAopByType= annotationConfigApplicationContext.getBean(TestAop.class);
        TestAop testAopByName= (TestAop) annotationConfigApplicationContext.getBean("testAop");
        TestAopService testAopServiceByName= (TestAopService) annotationConfigApplicationContext.getBean("testAopServiceImpl");
//        TestAopServiceImpl testAopServiceByType=annotationConfigApplicationContext.getBean(TestAopServiceImpl.class);
        testAopByName.showTxt();
        taAopByType.showTxt();
//        testAopServiceByType.showTxt();
        testAopServiceByName.showTxt();
    }
}


// spring的配置类 proxyTargetClass属性为false
@Configuration
@ComponentScan
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class SpringConfig {

}

依旧先看现象再分析原因

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第2张图片

执行到这一行时,main方法里已经拿到了所有需要测试的对象,TestAop对象使用beanName获取(taAopByName)和使用类型获取(taAopByType)以及TestAopServiceImpl使用类型获取(testAopServiceByName),被注释掉的是使用beanType获取的方式,解开会报错,原因接下来会分析.先看看走到这一步时获取到的对象是什么类型,结果如图所示

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第3张图片

taAopByType与taAopByName都是cglib代理类型,testAopServiceByName是jdk动态代理类型

先把代码走完,回头再看原因,运行结果如图

配置的aop没有都生效了,没有生效的是配置为this(demo.AOP.TestAopServiceImpl)切点的代理类,具体原因之后再看

接下来看看代理的过程,aop代理对象是在bean初始化的时候生成的,所以找到AbstractAutowireCapableBeanFactory 中的初始化bean方法

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第4张图片

进入bean后置处理器应用方法,applyBeanPostProcessorsAfterInitialization 方法会处理所有的BeanPostProcessors,也就是说所有实现了BeanPostProcessors接口的方法都会在这里处理,包括自己实现的方法(前提是交由spring处理),所有的后置处理器如下

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第5张图片

可以看到spring自己实现了若干后置处理器,而涉及到aop代理对象生成的后置处理器就是图中被标注的这条.接下来进入这条后置处理器的方法中,看看到底干了什么.

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第6张图片

实际上生成代理对象的方法就这这里面

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第7张图片

最后找到了DefaultAopProxyFactory 默认的aop代理工厂,而关键就这这里.

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第8张图片

上图中被标注的判断条件中包含config.isProxyTargetClass(),而这个就是spring配置类中注解中的属性,默认为false.

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第9张图片

代码下面的逻辑就是判断生成的代理类型是jkd动态代理或是cglib动态代理,可以看到即使ProxyTargetClass配置了true想使用cglib代理也得判断被代理对象是否是接口,如果是接口或者是个代理对象,则会使用jdk动态代理,否则进入cglib动态代理.

那么回到之前未解决的问题,

第一,为什么main方法中annotationConfigApplicationContext.getBean(TestAopServiceImpl.class)这一行解开会报错?

第二,为什么切点配置this(demo.AOP.TestAopServiceImpl)不会生效?

文章开头说的四点结论,其中的

2:jdk动态代理的对象通过继承Poxy对象并实现被代理对象的接口来完成代理(重点)

3:cglib动态代理通过继承被代理对象来完成代理

关键点就在两种代理的方式不同,使用jdk动态代理的对象,会使用继承Poxy对象并实现被代理对象的接口来完成代理

也就是       

extend Poxy implements TestAopService  

那么生成的代理对象就不再是TestAopServiceImpl 类型对象了,该对象只是和TestAopServiceImpl一样实现了TestAopService接口罢了,而切点this(demo.AOP.TestAopServiceImpl)恰恰就是监测对象是否为TestAopServiceImpl类型,所以增强方法没有生效!

那么解决办法呢?

答:使用cglib动态代理

之前配置类中的proxyTargetClass属性为false,在源码中也看到,这个属性其实可以影响动态代理生成的类型,将这个属性设置为true

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第10张图片

此时各个代理对象的类型如下

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第11张图片

可以看到之前使用的jdk动态代理变成了cglib动态代理,运行结果如下:

Spring Aop 使用 ,Aop代理对象的获取, jdk动态代理,cglib动态代理的区别_第12张图片

this(demo.AOP.TestAopServiceImpl) 生效了!

 

你可能感兴趣的:(spring)