一、背景
在 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断点看逻辑