前言
spring 中aop是一个核心概念,spring boot 是如何实现自动化配置的?现在我们就来分析一下
解析
spring boot 中自动化配置是读取/META-INF/spring.factories 中读取org.springframework.boot.autoconfigure.EnableAutoConfiguration配置的值,其中有关aop的只有一个,即org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.该类就是打开宝箱的钥匙
AopAutoConfiguration 声明了如下注解:
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
- @Configuration –> 配置类
- @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class }) –> 在当前的类路径下存在EnableAspectJAutoProxy.class, Aspect.class, Advice.class时该配置才被解析
- @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true) –> 当配置有spring.aop.auto= true时生效.如果没有配置,则默认生效
AopAutoConfiguration中只有2个内部类:
JdkDynamicAutoProxyConfiguration,代码如下:
@Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true) public static class JdkDynamicAutoProxyConfiguration { }
- @Configuration –> 配置类
- @EnableAspectJAutoProxy(proxyTargetClass = false) –> 开启aop注解,关于该注解,后面有解析.
- @ConditionalOnProperty(prefix = “spring.aop”, name = “proxy-target-class”, havingValue = “false”, matchIfMissing = true)–> 当配置有spring.aop.proxy-target-class= false时生效,如果没有配置,默认生效.
CglibAutoProxyConfiguration,代码如下:
@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false) public static class CglibAutoProxyConfiguration { }
- @Configuration –> 配置类
- @EnableAspectJAutoProxy(proxyTargetClass = false) –> 开启aop注解
- @ConditionalOnProperty(prefix = “spring.aop”, name = “proxy-target-class”, havingValue = “true”, matchIfMissing = false)–> 当配置有spring.aop.proxy-target-class= true时生效,如果没有配置,默认不生效.
因此,我们可以知道,aop 默认生效的JdkDynamicAutoProxyConfiguration.这也符合我们关于spring aop的认知,默认是使用jdk,如果使用面向类的代理,则需要配置spring.aop.proxy-target-class=true,来使用cglib进行代理
@EnableAspectJAutoProxy 代码如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { // 表明是否使用基于类的代理,false--> 使用面向接口的代理(java 动态代理).true--> 使用cglib boolean proxyTargetClass() default false; // 表明是否暴露代理(通过ThreadLocal的方式),该属性设置为true可解决目标对象内部的自我调用将无法实施切面中的增强的问题. boolean exposeProxy() default false; }
关于目标对象内部的自我调用将无法实施切面中的增强的问题可看Spring事务处理时自我调用的解决方案及一些实现方式的风险 这篇博客
这里通过@Import(AspectJAutoProxyRegistrar.class),导入了AspectJAutoProxyRegistrar的配置.
还是由之前的文章可知,此时会调用ConfigurationClassParser#processImports.由于AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,因此会加入到JdkDynamicAutoProxyConfiguration所对应的ConfigurationClass 的importBeanDefinitionRegistrars中.
最后,在ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars会对importBeanDefinitionRegistrars依次调用其registerBeanDefinitions 方法.
AspectJAutoProxyRegistrar#registerBeanDefinitions 代码如下:
public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } }
注册id为org.springframework.aop.config.internalAutoProxyCreator,class 为 AnnotationAwareAspectJAutoProxyCreator的bean.这里经过层层调用,最终调用AopConfigUtils#registerOrEscalateApcAsRequired方法,代码如下:
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 如果已经存在了自动代理创建器且存在的自动代理创建器与现状的不一致那么需要根据优先级来判断到底需要使用哪一个 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { // 改变bean最重要的就是改变bean所对应的ClassName apcDefinition.setBeanClassName(cls.getName()); } } // 如果已经存在自动代理创建器并且与将要创建的一致,那么无需再次创建 return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
- 这里传入的cls 为AnnotationAwareAspectJAutoProxyCreator.class
- 如果已经存在了自动代理创建器且存在的自动代理创建器与现状的不一致那么需要根据优先级来判断到底需要使用哪一个–> 如果此时传入的AnnotationAwareAspectJAutoProxyCreator的优先级比已注册的优先级高,则替换为AnnotationAwareAspectJAutoProxyCreator.然后直接return
- 如果已经存在自动代理创建器并且与将要创建(AnnotationAwareAspectJAutoProxyCreator)的一致,那么无需再次创建
- 进行注册,id为org.springframework.aop.config.internalAutoProxyCreator,class 为 AnnotationAwareAspectJAutoProxyCreator,优先级为Integer.MIN_VALUE.角色为内部使用
如果配置了proxyTargetClass等于true,则对id为org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition 添加proxyTargetClass 的属性,值为true.代码如下:
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); } }
如果配置了exposeProxy等于true,则对id为org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition 添加exposeProxy 的属性,值为true. 代码如下:
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); } }
关于aop方面的源码,笔者准备在spring深度揭秘中一一道来,关于spring boot 中的aop 自动化配置就解析到这里.