您现在的位置是:首页 >其他 >Spring AOP源码分析篇一:@EnableAspectJAutoProxy的来龙去脉网站首页其他

Spring AOP源码分析篇一:@EnableAspectJAutoProxy的来龙去脉

程序源仔 2024-06-17 11:25:15
简介Spring AOP源码分析篇一:@EnableAspectJAutoProxy的来龙去脉

目录

前言

1. 名词概念

2. @EnableAspectJAutoProxy的来龙去脉

3. 代码例子

4. 源码解读

4.1. parser.parse(candidates)

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所以执行的其实是AbstractAutoProxyCreatorpostProcessAfterInitialization方法,这个类就是和AOP相关的

3、红色部分(生成代理对象)

  • AbstractAutoProxyCreatorpostProcessAfterInitialization方法中
  • 会先通过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集合中的

AspectJAutoProxyRegistrarregisterBeanDefinitions()方法,这个方法会把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博客

好玩Spring之@EnableAspectJAutoProxy解读_煎饼皮皮侠的博客-CSDN博客

Spring AOP 第三篇-由点到面全面解析Spring AOP_Cison chen的博客-CSDN博客

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。