spring之我见 - Spring AOP实现原理(上)

前言

以前写过一篇文章, 专门讲了spring的动态代理实现原理 从代理模式再出发!Proxy.newProxyInstance的秘密, 这一次我们探究下动态代理模式比较重量级的应用 – Spring AOP 的源码实现. 本文重在讲主流程, 为了让流程更清楚, 本篇会补上很多Spring IOC 的知识.

ConfigurationClassPostProcessor

ConfigurationClassPostProcessor的由来

都知道要用 Spring AOP, 需要先引入 spring-boot-starter-aop 的组件, 我们用的 Spring boot 版本是 2.4.4 , 接下来就说明下这个组件是如何启用的.

首先我们介绍IOC组件中比较重要的类 – BeanDefinitionRegistryPostProcessor , 先看一下类注释:

拓展了标准的 BeanFactoryPostProcessor SPI 机制, 允许在 BeanFactoryPostProcessor 介入操作前 注册bean definition 到容器中. 

Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.

然后总结下看下文之前必须要知道的三个类:

  • BeanDefinitionRegistryPostProcessor 侧重于注册BeanDefinition对象的信息. 这是spring对外开放的一个拓展(SPI),方便第三方注册自己的组件到Spring IOC.
  • BeanFactoryPostProcessor 侧重于修改BeanDefinition对象的信息.
  • BeanDefinitionRegistry 是管理 BeanDefinition的大总管,当然也可以注册修改BeanDefinition对象的信息 ,是 beanfactory 必须实现的接口.

知道了 BeanDefinitionRegistryPostProcessor 的作用, 然后我们介绍本节的主角 – ConfigurationClassPostProcessor

ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor, 它的功能在于处理@Configuration class, 试图通过@Configuration class 找到更多需要被IOC管理的类对象. 这个类是整个spring启动流程最先注册实例化的类.下面是它的注册过程:

//从 SpringApplication main 方法出发, 跟踪到createApplicationContext()方法, 这是在创建ApplicationContext. 
- class SpringApplication 
    - run(String... args) 
        - createApplicationContext()


//然后初始化了一个 AnnotationConfigApplicationContext(),这个初始化方法很重要, 它往spring容器注册了ConfigurationClassPostProcessor,
public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

//继续初始化  AnnotatedBeanDefinitionReader, 最后执行 registerAnnotationConfigProcessors(),这个方法直接把ConfigurationClassPostProcessor注册成bean definition放到beanDefinitionMap中. 顺便多一嘴,  AnnotationConfigApplicationContext实现了BeanDefinitionRegistry接口.所以按照前文所说, 它是BeanDefinition 的大管家,就有能力管理所有的  BeanDefinition.  `BeanDefinitionRegistryPostProcessor` 和 `BeanDefinitionRegistry` 这些概念别搞混了.
....
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
....

启动类不能忘

到目前为止, 已经把 ConfigurationClassPostProcessor 的由来说明白了, 现在继续讲ConfigurationClassPostProcessor干的事情.之前说过, ConfigurationClassPostProcessor 主要是处理@Configuration class, 那理论上第一个应该被ConfigurationClassPostProcessor 处理的类是什么? 应该是 执行main函数的启动类 .

在启动类上, @SpringBootApplication内部其实有几个关键的注解, 我们现在先关注@Configuration,说明DemoApplication类也是一个配置类,需要被ConfigurationClassPostProcessor 处理. 那么 DemoApplication 什么时候注册成bean definition,然后放到beanDefinitionMap中呢?


//run()方法有两个参数,参照注释, 一个是启动类的class对象(primarySource), 一个是启动参数. 这个class对象就很关键.
/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}


//构造器里把primarySources赋值给局部变量.
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		....
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		....
	}


//经过一系列调用链把primarySources注册成bean definition,这里省略很多,大家可以自行调试
- class SpringApplication 
    - run(String... args) 
        - prepareContext(...)
            - load(...)
                - loader.load()
                    ...
                        - registerBean

invokeBeanFactoryPostProcessors

在这里插入图片描述

到此为止, IOC容器一共注册了7个BeanDefinition, 包括了2个对目前流程最重要的类

  • org.springframework.context.annotation.internalConfigurationAnnotationProcessor(ConfigurationClassPostProcessor)
  • com.example.demo.DemoApplication (启动类)

现在我们看看 ConfigurationClassPostProcessor 是如何起作用的. 对于spring 的IOC流程, 我们应该知道大名鼎鼎的refresh()方法, 它解析我们给的ioc配置(java-base config,xml etc),并生成对象. 在执行 invokeBeanFactoryPostProcessors 的时候, 就会遍历 之前预置的 BeanDefinitionRegistryPostProcessor 对象

	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
					.tag("postProcessor", postProcessor::toString);
			postProcessor.postProcessBeanDefinitionRegistry(registry);
			postProcessBeanDefRegistry.end();
		}
	}  


//正式进入ConfigurationClassPostProcessor 的 processConfigBeanDefinitions 方法
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

        //这里获取到  到目前位置找到的7个BeanDefinition, 逐个循环找 Configuration classes
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
            //具体的查找逻辑,选取里面重要的一行判断,看到 metadata.getAnnotationAttributes(Configuration.class.getName()) 是不是就一目了然了. 
            // Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

        //找到 demoApplication 是Configuration classes
		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}


        // 之前大家用的很多的 @Order, 会在这里进行重新排序
        // Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});


        //ConfigurationClassParser 开始解析 demoApplication ,解析的目的是为了获得 demoApplication 导入的其它对象
        do {
			ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

            ....
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			parser.parse(candidates);
			parser.validate();
            ....
            }
		while (!candidates.isEmpty());

继续跟代码,doProcessConfigurationClass 方法比较关键,我们都知道 @ComponentScan 注解很关键,它几乎扫描全项目所有的需要 Spring 托管的对象,而 @ComponentScan 就在下述代码中被扫描处理,ComponentScan 的扫描细节就直接忽略。

- parse: ConfigurationClassParser
	- doProcessConfigurationClass: ConfigurationClassParser
	    - doScan: ClassPathBeanDefinitionScanner
	        - ...


	//doProcessConfigurationClass 代码逻辑中扫描 Configuration class 是否有 @ComponentScan,有的话就扫描注册所有的 @Component 对象
	// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		//扫描 Configuration class 是否有 @Import,有的话就注册@Import要导入的类
		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		//同理处理 @ImportResource
		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

AopAutoConfiguration

经过 @ComponentScan 扫描,类 TestAspectJTestService 被Spring找到并注册到 beanDefinitionMap( TestAspectJTestService 的内容这一篇并不重要,下一篇会展示出来), 但是以前用过 AOP 的都知道, 开启 Spring AOP 需要一个注解 @EnableAspectJAutoProxy,在启动类 DemoApplication 上并没有标注这个注解,因为 spring boot 有一个 spring-boot-autoconfigure 模块, autoconfigure 会自动帮我们启用一些组件, 就比如我们现在需要的 aop 模块

我们先看看 spring-boot-autoconfigure 模块里面的 AopAutoConfiguration 代码

//这个配置默认是开启的,因为从 AopAutoConfiguration的注解 @ConditionalOnProperty来看, `spring.aop.auto matchIfMissing` 为true, 说明`spring.aop.auto`不存在显性配置的时候, 条件就是成立的, 而且根据 `spring.aop.proxy-target-class` 的设置,默认会先启用CglibAutoProxyConfiguration,也就是使用cglib来实现aop的逻辑
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}

	}
}

那上面的 AopAutoConfiguration 是怎么被 Spring 启动的时候加载的呢? 我们还要接着上章 doProcessConfigurationClass 方法说起,之前它解析了启动类 DemoApplication 的 @ComponentScan 注解,下面还继续解析了 @Import 注解

class ConfigurationClassParser doProcessConfigurationClass

// Process any @Import annotations
// 这个方法处理了 DemoApplication 里面的 @Import(AutoConfigurationImportSelector.class) 注解,并把 AutoConfigurationImportSelector 对象放进了 deferredImportSelectors list中。 
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);


// 等 DemoApplication 被彻底 parse 完成,在方法返回时,会最后处理  deferredImportSelectors list . 
public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				......
			}
		......
		}

		//最后处理 deferredImportSelectors list 中的元素
		this.deferredImportSelectorHandler.process();
	}

//解析deferredImportSelectors list的流程逻辑
 - this.deferredImportSelectorHandler.process();
	- processGroupImports()
		- grouping.getImports()
			- this.group.process
				  //回调AutoConfigurationImportSelector的getAutoConfigurationEntry方法, 找到 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
				- AutoConfigurationImportSelector getAutoConfigurationEntry
					//因为 AopAutoConfiguration 也有注解 @Configuration, 所以按照 @Configuration class的逻辑去处理(执行跟启动类DemoApplication一样的逻辑)
					- processConfigurationClass()
				
		

这里再稍微细究下AutoConfigurationImportSelector的getAutoConfigurationEntry方法怎么找的。

SpringFactoriesLoader 类专门负责轮询扫描所有的 META-INF/spring.factories 文件,当你传参 org.springframework.boot.autoconfigure.EnableAutoConfiguration时,就会从 spring-boot-autoconfigure 包里的 META-INF/spring.factories 搜索 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的对象列表.

在这里插入图片描述
在这里插入图片描述

@Import 导入的类并没有直接被注册到 beanDefinitionMap,等 demoApplicationConfigurationClassParser 解析完毕后,通过 this.reader.loadBeanDefinitions(configClasses) 方法注册到 beanDefinitionMap。

....
	parser.parse(candidates);
	parser.validate();

	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
	configClasses.removeAll(alreadyParsed);

	// Read the model and create bean definitions based on its content
	if (this.reader == null) {
		this.reader = new ConfigurationClassBeanDefinitionReader(
				registry, this.sourceExtractor, this.resourceLoader, this.environment,
				this.importBeanNameGenerator, parser.getImportRegistry());
	}
	//将parser解析结果注册到 beanDefinitionMap 
	this.reader.loadBeanDefinitions(configClasses);
	alreadyParsed.addAll(configClasses);
....

我们总结下这一章, 首先 spring aop 的启用得益于 spring-boot-autoconfigure 模块. 而 spring-boot-autoconfigure 模块是通过启动类的@Import(AutoConfigurationImportSelector.class) 导入的. 所以说别看启动类就一个文件,它在项目启动过程中做了很重要的角色.

AnnotationAwareAspectJAutoProxyCreator

上面我们千辛万苦把 JdkDynamicAutoProxyConfiguration 注册到 beanDefinitionMap(默认应该是启用CglibAutoProxyConfiguration,但是我们配置了 spring.aop.proxy-target-class=false ), JdkDynamicAutoProxyConfiguration本身并不重要,重要的在于里面的 @Import(AspectJAutoProxyRegistrar.class),在 this.reader.loadBeanDefinitions(configClasses) 的过程中,会执行 registerBeanDefinitions 回调方法. 并且把 AnnotationAwareAspectJAutoProxyCreator 对象注册进来.

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		// 往beandefinition注册 `AnnotationAwareAspectJAutoProxyCreator`
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

	}

}

AnnotationAwareAspectJAutoProxyCreator 是一个 InstantiationAwareBeanPostProcessor(BeanPostProcessor)对象,这个对象作用非常大, 我们看一下它的注释, 处理所有的@AspectJ修饰过的类,为什么要处理这个类? 因为所有的 AOP 逻辑(代理谁,代理的逻辑)都写在@AspectJ修饰过的类里, Spring需要一个专门的组件来收集处理我们在@AspectJ类定义的AOP信息, 而这就是AnnotationAwareAspectJAutoProxyCreator需要处理的事.

处理所有的@AspectJ修饰过的类

AspectJAwareAdvisorAutoProxyCreator subclass that processes all AspectJ annotation aspects in the current application context.

料已备好,等待下菜

这一篇我们并没有说 AOP 在 Spring 是怎么大显神威的. 而是先介绍了一系列我们接下来必不可少的组件. 这些组件对于理解 Spring 支持 AOP 特别重要. 我们再简单复习下:

  • ConfigurationClassPostProcessor. Spring 启动首批注册的组件, 专门用来处理 @Configuration class, 正因为有它,才能引出其它待引入的组件
  • AutoConfigurationImportSelector. 写在启动类@Import里, ConfigurationClassPostProcessor 解析启动类的时候会回调 AutoConfigurationImportSelectorgetAutoConfigurationEntry 方法.从而引出 spring-boot-autoconfigure 模块.
  • AopAutoConfiguration, 隶属于 spring-boot-autoconfigure 模块中的一个类,专门用来提供 CglibAutoProxyConfigurationJdkDynamicAutoProxyConfiguration , 这两者是实现AOP的核心类.
  • AnnotationAwareAspectJAutoProxyCreator. AopAutoConfiguration 顺便也通过 @import 引入了 AspectJAutoProxyRegistrar, AspectJAutoProxyRegistrar 又注册了 AnnotationAwareAspectJAutoProxyCreator, AnnotationAwareAspectJAutoProxyCreator 负责找出所有 @AspectJ修饰过的对象,为实现 AOP 做准备.

下一篇,将继续讲实现 AOP 的流程.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值