springboot2.x AOP 默认使用Cglib 源码

一、背景

在 SpringBoot 2.x AOP中会默认使用Cglib来实现,但是Spring5中默认还是使用jdk动态代理。Spring AOP 默认使用 JDK 动态代理,如果对象没有实现接口,则使用 CGLIB 代理。也可以强制使用 CGLIB 代理。springboot默认使用cglib实现代码逻辑来记录一下。

二、自动配置

1、springboot aop自动配置类为AopAutoConfiguration.java

@Configuration
@ConditionalOnClass({EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class})
@ConditionalOnProperty(
    prefix = "spring.aop",
    name = {"auto"},
    havingValue = "true",
    matchIfMissing = true
)
public class AopAutoConfiguration {
    public AopAutoConfiguration() {
    }

    @Configuration
    @EnableAspectJAutoProxy(
        proxyTargetClass = true
    )
    @ConditionalOnProperty(
        prefix = "spring.aop",
        name = {"proxy-target-class"},
        havingValue = "true",
        matchIfMissing = true
    )
    public static class CglibAutoProxyConfiguration {
        public CglibAutoProxyConfiguration() {
        }
    }

    @Configuration
    @EnableAspectJAutoProxy(
        proxyTargetClass = false
    )
    @ConditionalOnProperty(
        prefix = "spring.aop",
        name = {"proxy-target-class"},
        havingValue = "false",
        matchIfMissing = false
    )
    public static class JdkDynamicAutoProxyConfiguration {
        public JdkDynamicAutoProxyConfiguration() {
        }
    }
}

2、 CglibAutoProxyConfiguration上注解

@ConditionalOnProperty(
prefix = “spring.aop”,
name = {“proxy-target-class”},
havingValue = “true”,
matchIfMissing = true
)
当spring.aop.proxy-target-class是否配置都会设置成true.关于ConditionalOnProperty注解参数可以参照这个博客

3、 @EnableAspectJAutoProxy(proxyTargetClass = true)这个注解中导入@Import(AspectJAutoProxyRegistrar.class)

在这里插入图片描述
可以看到把beanName:org.springframework.aop.config.internalAutoProxyCreator
,类型为:AnnotationAwareAspectJAutoProxyCreator的proxyTargetClass 属性设置成true。
在这里插入图片描述

三、bean在spirng中被代理过程

1、不存在循环依赖的时候

bean属性赋值后,发生在bean initializeBean初始化方法applyBeanPostProcessorsAfterInitialization后置处理中。
执行AbstractAutoProxyCreator的后置处理方法,如果没有调用过三级缓存earlyProxyReferences中就没有存储当前对象名称。如图1
在这里插入图片描述
wrapIfNecessary->createProxy-> proxyFactory.getProxy
在这里插入图片描述

在这里插入图片描述

当这三个条件都满足时,会使用jdk动态代理.
!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)
看一下ProxyConfig和AnnotationAwareAspectJAutoProxyCreator的继承关系,可以发现自动配置中设置的ProxyTargetClass=true其实就是设置父类中proxyConfig中的属性。config.isProxyTargetClass()是true,所以执行cglib动态代理创建。isOptimize配置文件可以配置,默认为false
hasNoUserSuppliedProxyInterfaces可以理解为判断被代理类是否有实现接口。
在这里插入图片描述

2、循环依赖的时候

populateBean属性赋值前,三级缓存中vulue存入函数式接口,this.singletonFactories.put(beanName, singletonFactory);当调用getObject方法时,会执行getEarlyBeanReference(beanName, mbd, bean)。前
先看一下getEarlyBeanReference这个方法,再看什么时候执行三级缓存中的函数式接口。遍历所有后置处理器,会执行AbstractAutoProxyCreator的getEarlyBeanReference方法,如下图可以看到earlyProxyReferences放入值,防止初始化中后置处理器会判断是有值,防止重复创建代理类,可以往上翻看图1。wrapIfNecessary方法和无循环依赖方法一样,这里不再赘述。
在这里插入图片描述
下面看一下什么时候会执行三级缓存里面的函数式接口。如果A中有属性B,B中有属性A,当创建A的时候,A在属性赋值前三级缓存存入A的创建代理类的函数式接口。当A属性赋值时,会创建B,因为B有属性A,又会重新进入创建A的逻辑,doGetBean->getSingleton
在这里插入图片描述
如果一级和二级缓存都没有值,就会获取三级缓存函数式接口,执行getObject(),返回代理对象,把代理对象存入二级缓存,最后返回代理对象完成B的属性A的赋值。A继续完成B的属性赋值把创建完成的类再放入一级缓存。可以看到如果有循环依赖会在属性赋值时完成代理类的创建,而不会到后置处理器。这也是三级缓存的作用就是完成循环依赖的创建代理对象。
在这里插入图片描述

四、jdk代理失效情况

1、 配置文件中spring.aop.proxy-target-class: false,1判断会是true,进入evaluateProxyInterfaces方法。

在这里插入图片描述## 2、继续看evaluateProxyInterfaces,重点看1中if判断条件,所有条件都满足,会进入2,而不会执行3,执行3又会把proxy-target-class设置为true.在这里插入图片描述

3、isConfigurationCallbackInterface方法中,实现了InitializingBean.java

DisposableBean.java、Closeable.java 、AutoCloseable.java、Aware.java,返回true。
在这里插入图片描述

4、ifc.getMethods().length > 0 就是接口中需要有方法。所以如果实现了3中的接口或接口中没有方法,会使用cglib代理。

5、例子

5.1创建没有方法的接口类

public interface TestService
{
}

5.2添加实现类

@Service
public class TestServiceImple implements TestService{

    public void test(){
        System.out.println("test");
    }
}

5.3创建切面

@Component
@Order(value = -100)
@Slf4j
@Aspect
public class TestAspect {


    @Pointcut("execution(* com.haier.cosmo.wms.service.service..*.*(..))")
    private void dbAspect() {
    }

    @Around("dbAspect()")
    public Object db(ProceedingJoinPoint joinPoint) {
        try {
            return  joinPoint.proceed();
        } catch (Throwable e) {
            throw new ServiceException(e.getMessage());
        }finally {
            SchemaContextHolder.clearSchema();
        }
    }
}

5.4断点看逻辑
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值