您现在的位置是:首页 >其他 >Spring AOP源码分析篇一:@EnableAspectJAutoProxy的来龙去脉网站首页其他
Spring AOP源码分析篇一:@EnableAspectJAutoProxy的来龙去脉
目录
2. @EnableAspectJAutoProxy的来龙去脉
4.2. this.reader.loadBeanDefinitions(configClasses);
前言
我一直都想做一个AOP的专题,奈何不知从何入手,思来想去还是觉得从@EnableAspectJAutoProxy注解开始讲起,毕竟它是AOP的开关,通过它来引出AOP的全流程以及后面系列文章的分享,这样比单纯讲理论来的容易懂,毕竟AOP里面的类和名词都特别多,大家不用担心文章难懂,我会加上一系列流程图来帮助大家理解,话不多说,开始今天分享!
1. 名词概念
先上代码:
上面通过代码演示,如果还对概念不清晰的话,下面还有流程图演示哦!
1、通知(advice):通知定义了切面是什么以及何时使用。可以在连接点做具体的代码处理,例如在方法前后(JoinPoint连接点)打印日志(前置通知、后置通知、环绕通知)、对方法的异常进行处理。
2、连接点(JoinPoint):spring允许你通知(Adivce)的地方。程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法前后、抛出异常时都可以是连接点。Spring只支持方法连接点。它和advice的区别是:例如有个A类的mehtod()方法,在该方法执行前让他进行前置通知,那“方法执行前”的位置就是连接点,因为连接点是围绕着具体的method(),所以JoinPoint对象可以获取到method()的各种属性,而前置通知的内容(处理逻辑),就是具体的代码逻辑。
3、切入点(Pointcut):多个连接点的集合的统称,例如@Pointcut( "execution(public void com.xiaoyuanzai.service.UserService.test())" ),代表了在这个方法前/后可以进行增强,每个位置就是一个连接点,这样算下来就有很多种可能,有一系列的连接点,这就是切入点了
4、切面(Aspect) :切面是通知和切点的结合
5、适配器(Advisor):适配器=通知(advice)+切入点(Pointcut)
太抽象了?给个图看看
1、因为@PointCut注解的范围可能是多个类,这些类有共同的方法,就例如图中有两个类适合,他们有三个方法同名,假设在代码中定义前置通知,这个通知在类A和类B的这三个方法都能使用,这样每个方法的前置部分的位置,就是连接点JoinPoint,而这些前置通知的JoinPoint的集合,就是pointcut
2、Aspect: JoinPoint+Advice组成了一个切面类
3、每个JointPoint对应一个Advice,可是一个Advice对应多个JointPoint
2. @EnableAspectJAutoProxy的来龙去脉
我们先看一下@EnableAspectJAutoProxy注解里面
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
划重点:这里面有@Import(AspectJAutoProxyRegistrar.class)注解,这个是关键!!!下面会讲到
因为下面涉及到@Import的知识点,如果想了解他具体的源码,可以看我之前的文章:
Spring之@Import注解使用和spring源码分析_程序源仔的博客-CSDN博客
看一下AOP核心流程图
看不清没关系,还有高清链接
aop全流程图| ProcessOn免费在线作图,在线流程图,在线思维导图
1、左边青色和紫色部分一起讲
- IOC容器启动时,调用了ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(registry),因为这个类继承了BeanFactoryPostProcessor,所以在这里会被执行。而@EnableAspectJAutoProxy是用来开启AOP的,他里面有@Import(AspectJAutoProxyRegistrar.class)注解,而postProcessBeanDefinitionRegistry里面就是负责扫描并导入了带有@Import注解的类
- AspectJAutoProxyRegistrar 类继承了ImportBeanDefinitionRegistrar接口,里面有个registerBeanDefinitions()方法要实现,他的作用是把AnnotationAwareAspectJAutoProxyCreator注册到容器中
- 而AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator,而AbstractAutoProxyCreator是一个BeanPostProcessor,在容器后面初始化实例对象后,会调用到他
- 至此,与AOP相关的处理器注册完成
2、右边青色部分
- 在调用getBean()的时候,会去执行bean的生命周期,实例化、属性赋值、初始化,等走到bean的初始化结束后,会去调用后置处理器,执行applyBeanPostProcessorsAfterInitialization()方法,这个后置处理器是BeanPostProcessor,切点(pointcut)对应的代理对象这时候就会被创建
- 而灰色部分注入的AnnotationAwareAspectJAutoProxyCreator就是继承了BeanPostProcessor,因为AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator所以执行的其实是AbstractAutoProxyCreator的postProcessAfterInitialization方法,这个类就是和AOP相关的
3、红色部分(生成代理对象)
- 在AbstractAutoProxyCreator的postProcessAfterInitialization方法中
- 会先通过getAdvicesAndAdvisorsForBean获取Bean对应的advisor数组(里面就是一系列通知advice)
- 这些advisor数组就是在Spring扫描定义好的@Aspect切面类时保存好的
- 然后将相关的advice作为参数,传到createProxy方法中,这一步生成一个代理对象
下面给出AnnotationAwareAspectJAutoProxyCreator的继承图
看图可得,继承关系是 BeanPostProcessor -> AbstractAutoProxyCreator -> AspectJAwareAdvisorAutoProxyCreator -> AnnotationAwareAspectJAutoProxyCreator
3. 代码例子
先定义一个UserService,这个就是目标类,要对他进行AOP
@Component
public class UserService {
public void test(){
System.out.println("advice test!!!");
}
}
生成一个切面类,里面包含了所有通知(advise)和切面(pointcut)
/**
* @Author xiaoyuanzai
* @Date 2023/5/12 20:33
* @description:
*/
@Aspect
@Component
public class MyAspect {
//定义一个切入点:指定哪些方法可以被切入(如果是别的类需要使用 请用该方法的全类名)
@Pointcut( "execution(public void com.xiaoyuanzai.service.UserService.test())" )
public void pointCut() {
}
@Before( "pointCut()" )
public void beforeAdvice( JoinPoint joinPoint ) {
System.out.println( "AOP Before Advice..." );
}
@After( "pointCut()" )
public void AfterAdvice( JoinPoint joinPoint ) {
System.out.println( "AOP After Advice..." );
}
@Around( "pointCut()" )
public void around( ProceedingJoinPoint joinPoint ) {
System.out.println( "AOP Aronud before..." );
try {
joinPoint.proceed();
} catch( Throwable e ) {
e.printStackTrace();
}
System.out.println( "AOP Aronud after..." );
}
@AfterThrowing( pointcut = "pointCut()", throwing = "error" )
public void afterThrowingAdvice( JoinPoint joinPoint, Throwable error ) {
System.out.println( "AOP AfterThrowing Advice..." + error );
System.out.println( "AfterThrowing..." );
}
// 环绕通知:此处有一个坑,当AfterReturning和Around共存时,AfterReturning是获取不到返回值的
// @AfterReturning(pointcut = "pointCut()", returning = "returnVal")
// public void afterReturnAdvice(JoinPoint joinPoint, Object returnVal) {
// System.out.println("AOP AfterReturning Advice:" + returnVal);
// }
}
xml文件定义bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<context:component-scan base-package="com.xiaoyuanzai"/>
<bean id="userService" class="com.xiaoyuanzai.service.UserService" />
</beans>
创建一个main方法启动容器
public class Test {
public static void main( String[] args ) throws InvocationTargetException, IllegalAccessException {
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext( AppConfig.class );
UserService userService = ( UserService )applicationContext.getBean( "userService" );
userService.test();
}
}
4. 源码解读
因为篇幅原因,这篇文章只讲解AnnotationAwareAspectJAutoProxyCreator的生成时机,至于它的调用时机放到下篇文章讲解
由上面讲解可知,要知道AnnotationAwareAspectJAutoProxyCreator的生成时机,我们的重点是看IOC容器怎么扫描到@Import(AspectJAutoProxyRegistrar.class)注解并获取到AnnotationAwareAspectJAutoProxyCreator对象注册到容器中
执行上面代码(简单的流程就不讲了)
上面截图主要就是这几步
1、AnnotationConfigApplicationContext # refresh()
2、AbstractApplicationContext # invokeBeanFactoryPostProcessors(beanFactory)
3、PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors())
4、PostProcessorRegistrationDelegate # invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup())
5、ConfigurationClassPostProcessor # postProcessBeanDefinitionRegistry(registry)
6、ConfigurationClassPostProcessor # processConfigBeanDefinitions(registry)
我们看一下processConfigBeanDefinitions()方法
其实核心就两步
1、parser.parse(candidates) 负责把AppConfig ->@EnableAspectJAutoProxy( )->@Import(AspectJAutoProxyRegistrar) 的 AspectJAutoProxyRegistrar扫描出来,放到集合importBeanDefinitionRegistrars中
2、this.reader.loadBeanDefinitions(configClasses) 这一步负责执行importBeanDefinitionRegistrars集合中的
AspectJAutoProxyRegistrar的registerBeanDefinitions()方法,这个方法会把AnnotationAwareAspectJAutoProxyCreator注册成beanDefinition集合中
看一下importBeanDefinitionRegistrars集合
Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> getImportBeanDefinitionRegistrars() {
return this.importBeanDefinitionRegistrars;
}
4.1. parser.parse(candidates)
顺着debug进去
doProcessConfigurationClass顾名思义,就是解析配置类的地方,我们文中的配置类是AppConfig
继续点进去
processImports就是解析@Import的地方,这里有个注意的地方,getImports(sourceClass),是获取这个配置类下面所有的@Import的类,我们点进去看一下
主角登场!
我们再回到processImports()方法看一下
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
//获取我们Import导入进来的所有组件
for (SourceClass candidate : importCandidates) {
// 如果import的类实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//实例化我们的SelectImport组件
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 如果import的是DeferredImportSelector,表示推迟导入
//
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 如果import的是普通的ImportSelector
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//asSourceClasses()方法的作用,将ImportSelector实现类变为普通类
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归解析-- 直到成普通组件,有可能import进来的类又有@Import注解,所以最终就到下面第三步的else那里去了
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 如果import的类实现了ImportBeanDefinitionRegistrar接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
//实例化我们的ImportBeanDefinitionRegistrar对象
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//实现ImportBeanDefinitionRegistrar接口的bean,放到类变量configurationClasses里面去了。
// currentSourceClass=所在配置类。这里还没有调用registerBeanDefinitions
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 如果import的类就是普通的类
else {
//1. 普通类,这里的普通类包括 @Import 直接导入的没有继承上面三个接口的类,
// 还有就是 @Import 导入了继承 ImportSelector 接口的类要注入的普通类
// 2.当做配置类再解析,注意这里会标记:importedBy, 表示这是Import的配置的类
// 3.再执行之前的processConfigurationClass()方法
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 注意,在asConfigClass方法中,不仅会将candidate生成一个ConfigurationClass,还会记录一下candidate是被哪个类导入的importedBy
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
上面一串代码大意是:
如果导入的是普通类,那么会直接把它当做配置类来解析
如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()
如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()
如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用
AspectJAutoProxyRegistrar在这里放进了importBeanDefinitionRegistrars集合中,等下处理
4.2. this.reader.loadBeanDefinitions(configClasses);
现在回到主流程,前面已经把AspectJAutoProxyRegistrar 拿到,现在要执行this.reader.loadBeanDefinitions(configClasses)方法了,下面图k可以看到有五个configClasses属性,估摸着应该有个遍历他们的过程,主要看遍历获取到AppConfig类就行
果不其然,就是遍历,点进去看遍历到AppConfig时的代码
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//判断我们经过解析的是不是通过@Import导入进来的
// 用的是 !this.importedBy.isEmpty() 判断
if (configClass.isImported()) {
// 将被导入的类生成BeanDefinition并注册到Spring容器中
// @Component的内部类,@Import所导入的类都是被导入的类
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//是不是通过我们的@bean导入进来的组件
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 处理@ImportResource("spring.xml")
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 处理ImportBeanDefinitionRegistrar,调用registerBeanDefinitions()方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
上面代码其实就是把前面扫描到的类,在这里进行注册,我们看最后一行loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())
很明显,这里又是一个集合,只不过这个集合只有AspectJAutoProxyRegistrar一个元素,肯定又是一个遍历
继续点进去就到了AspectJAutoProxyRegistrar类的,就不截图了,直接以代码的形式在下面展示给你们看流程
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册一个AnnotationAwareAspectJAutoProxyCreator类型的Bean,beanName为AUTO_PROXY_CREATOR_BEAN_NAME
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 修改AnnotationAwareAspectJAutoProxyCreator中对应的属性
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
//AopConfigUtils类的方法
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
//AopConfigUtils类的方法
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 这里我们看到,它传入了 AnnotationAwareAspectJAutoProxyCreator ,这正是我们寻找的
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
//AopConfigUtils类的方法
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//判断是否已经注册过该bean定义
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;
}
//实例化一个bean定义
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;
}
从上面代码可知,AnnotationAwareAspectJAutoProxyCreator被注册成了beanDefinition!!!!
注册完后,就到了getBean()时的生命周期--初始化结束后调用beanPostProcessor的后置处理器,这个时候会用上这个类来生成AOP代理对象,下节课分享这方面内容,不过可以先贴个图预告一下
advisor=(advice+pointCut),我们可以简单的理解为advice和advisor一一对应,这样看下面代码就不迷糊
最后:作为图灵学院一份子,学习来源于周瑜和徐庶两位我特别敬重的老师,站在巨人的肩膀上增加了自己的理解,本文不以商业化为目的,纯个人学习使用
文献参考:
Spring源码浅析之AOP、Aspect、Advice_aspectjafteradvice_DaHuangXiao的博客-CSDN博客