@EnableAspectJAutoProxy
@EnableAspectJAutoProxy
注解是Spring AOP开启的标志,在启动类标记此注解,即可加载对应的切面类逻辑.此注解的ElementType
为TYPE
,表示标记在类上。同时用@Retention(RetentionPolicy.RUNTIME)
声明了注解在运行时得到保留。此外最重要的是使用了@Import(AspectJAutoProxyRegistrar.class)
来注册AOP的
proxyTargetClass
@EnableAspectJAutoProxy
支持处理标有AspectJ的@Aspect批注的组件,用户可以主动声明proxyTargetClass
来指定Spring AOP
使用哪种动态代理方式来创建代理类(默认使用基于实现接口的JDK动态代理方式).
- 使用CGLIB动态代理来创建代理类
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
@ComponentScan("com.foo")
public class AppConfig {
// ...
}
exposeProxy
为了解决一些由于代理引发的切面失效问题,Spring AOP
在Spring 4.3.1后引入了AopContext
类来将代理类的引用存储在ThreadLocal中,通过AopContext
可以快速获取当前类的代理类.
默认为不支持,如果声明为true,即可使用AopContext
获取代理类.
同时,为了使用AspectJ,需要确保当前jar仓库存在
aspectjweaver
.
通过@Import注册AspectJAutoProxyRegistrar
通常情况下,我们的启动类本身也是一个Bean,Spring支持使用@Import
来导入一个没有标记任何Spring 注解的类来将该Java类注册成Spring的Bean.
@EnableAspectJAutoProxy
注解正是通过@Import
的方式来将AspectJAutoProxyRegistrar
类注册成Spring的Bean,以便在容器解析切面类时派上用场.
那么AspectJAutoProxyRegistrar
类的作用是什么?
我们从JavaDoc中可以看到这样一句话:
Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation.
根据当前BeanDefinitionRegistry在适当的位置注册AnnotationAwareAspectJAutoProxyCreator。
UML
![](https://img.haomeiwen.com/i19836894/1f898548aff009e9.png)
-
ImportBeanDefinitionRegistrar: 用来导入一些特殊的BeanDefinition,Spring在处理
@Configuration
时,会去扫描是否有通过@Import
标签导入的类,对ImportBeanDefinitionRegistrar
这类接口,还会执行其中的registerBeanDefinitions
方法. -
AspectJAutoProxyRegistrar: 实现了
ImportBeanDefinitionRegistrar
接口,用来注册AspectJAnnotationAutoProxyCreator
,也就是支持注解驱动(同时兼容XML)解析的AspectJ自动代理创建器.
AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 向容器注册AspectJAnnotationAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
// 如果@EnableAspectJAutoProxy上存在标签内容
if (enableAspectJAutoProxy != null) {
// 如果proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 是否开启exposeProxy特性
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
这里关键的地方就是通过
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
向容器注册AspectJAnnotationAutoProxyCreator
.
随后解析@EnableAspectJAutoProxy
注解上的元数据来决定是否开启上述我们讲到的proxyTargetClass
和exposeProxy
特性.
为了了解registerBeanDefinitions
方法的执行链路和调用时机,我们使用IDE的debug来查看调用栈分析执行流程.
- refresh中激活后置处理器ConfigurationClassPostProcessor加载@Configuration上的元数据
![](https://img.haomeiwen.com/i19836894/73d04040418d3139.png)
- 首先容器会加载refresh方法.
- 执行
invokeBeanFactoryPostProcessors(beanFactory);
激活工厂级别的后置处理器. - 由于启动类都是被
@Configuration
标记的,Spring会使用ConfigurationClassPostProcessor
来解析被@Configuration
的类. - 使用
ConfigurationClassBeanDefinitionReader
来加载配置类解析成BeanDefinition. - 在
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
中,会执行loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
这行代码,从代码的语义上我们可以大致可以猜出来,这是解析当前配置类上是否存在通过@Import
导入的实现了ImportBeanDefinitionRegistrar
的类,
![](https://img.haomeiwen.com/i19836894/b9c10624f118d7dc.png)
- 回调registrar的
registerBeanDefinitions
方法. - 执行
AspectJAutoProxyRegistrar#registerBeanDefinitions
方法.
- AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
使用IDE跳进AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
中,向下跳2层,来到AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
方法中.
- org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
这里我们发现了一个新的角色-
AnnotationAwareAspectJAutoProxyCreator
,该类就是Spring用来处理应用上下文中被@AspectJ
注解标记的类的.继续进入registerOrEscalateApcAsRequired
方法中看看注册流程.
- org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 当前容器是否包含 org.springframework.aop.config.internalAutoProxyCreator
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) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 将传入的class包装成BeanDefinition,然后注册到容器中,并讲其order的执行顺序调整为最优
// 在aop中,这里会注册AnnotationAwareAspectJAutoProxyCreator.class这个类
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;
}
这里的逻辑较为清晰.
首先查看当前容器中是否包含org.springframework.aop.config.internalAutoProxyCreator
的BeanDefiniton.
如果没有,将传入的class(在此处传入了AnnotationAwareAspectJAutoProxyCreator.class
)包装成RootBeanDefinition
,然后注册到容器中.
- 设置proxyTargetClass与exposeProxy
这个也是比较清晰的,这里简单讲一下.我们看一下如何设置
proxyTargetClass
即可,大体上设置proxyTargetClass与exposeProxy的逻辑都是相通的.
// 如果@EnableAspectJAutoProxy上存在标签内容
if (enableAspectJAutoProxy != null) {
// 如果proxyTargetClass为true,则强制指定AutoProxyCreator使用CGLIB进行代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 是否开启exposeProxy特性
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
进入
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
- AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
// 如果容器中包含 org.springframework.aop.config.internalAutoProxyCreator
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 取出 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 设置proxyTargetClass为true
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
如果容器中包含名为
org.springframework.aop.config.internalAutoProxyCreator
的BeanDefinition,那么取出该BeanDefinition,设置proxyTargetClass
为true
.
这里随便提及一下,Spring为了兼容不同的BeanDefinition持有不同的属性值,将它们都抽象成了MutablePropertyValues
,所以definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
就跟我们平时JavaBean中的set方法是一样的.
简单理解@Import和ImportBeanDefinitionRegistrar
下面我们通过两个用例来理解@Import
和ImportBeanDefinitionRegistrar
通过@Import导入类让Spring进行管理
public class NeedImportBean {
public void doSomething(){
Logger.getGlobal().info("Through @Import registry to a bean ");
}
}
- 在启动类中将NeedImportBean导入
/**
* @author jaymin
* 2020/11/30 20:13
*/
@Configuration
@ComponentScan(value = "com.xjm")
@Import(NeedImportBean.class)
@EnableAspectJAutoProxy
public class ApplicationConfig {
public static AnnotationConfigApplicationContext getApplicationContext() {
return new AnnotationConfigApplicationContext(ApplicationConfig.class);
}
}
- 对NeedImportBean做getBean
public class BeanFactoryDemo {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = ApplicationConfig.getApplicationContext();
NeedImportBean needImportBean = applicationContext.getBean(NeedImportBean.class);
}
}
- 输出结果
![](https://img.haomeiwen.com/i19836894/b2b50985c93e6740.png)
可以看到,通过@Import可以将没有被Spring注解标记的类进行注册,注册后即可当成普通的Bean来使用。
实现ImportBeanDefinitionRegistrar
现在我们换一种方式,让NeedImportBean实现ImportBeanDefinitionRegistrar接口,然后验证两个点:
- 是否会回调registerBeanDefinitions方法。
- 通过getBean是否能获取NeedImportBean
public class NeedImportBean implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Logger.getGlobal().info("Through implements ImportBeanDefinitionRegistrar and @Import to callback me.");
}
public void doSomething(){
Logger.getGlobal().info("Through @Import registry to a bean ");
}
}
- Result
测试的方法于上面一致.这里不重复贴了.
![](https://img.haomeiwen.com/i19836894/b6cc843b46e44bdf.png)
![](https://img.haomeiwen.com/i19836894/6110867e6a3cca62.png)
这里,我们验证了:
- 实现了ImportBeanDefinitionRegistrar接口后会执行回调
- 实现了ImportBeanDefinitionRegistrar接口后,通过
@Import
并不会注册成Bean.
扩展阅读
在搜寻资料的过程中,发现此文章对ImportBeanDefinitionRegistrar
解析的比较透彻.有兴趣可以阅读一下:
点我前往
总结
![](https://img.haomeiwen.com/i19836894/5b8729247b0acf27.png)
网友评论