当前位置: 首页 > article >正文

Spring之我见 - 从IOC谈到AOP实现原理

前言

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

前菜:IOC的相关知识

如何找到需要被AOP注解的类?

ConfigurationClassPostProcessor(BeanDefinitionRegistryPostProcessor)

大部分时候我们只知道注解扫描注入的方便性,但是细究的话还是可以看看 Spring 怎么找到这些类的,比如在这个题目下如何找到需要被代理的对象?

首先我们介绍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 的属性进行修改,虽然也可以注册bean。
  • 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 中。
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));
}
@Configuration

到目前为止, 已经把 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

至此,启动类也被注册成了 bean definition,供下文 ConfigurationClassPostProcessor 扫描使用。

@ComponentScan

在这里插入图片描述

到此为止, 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 对象(包括ConfigurationClassPostProcessor)。

    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 方法。
//正式进入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 托管的对象(所有被 @Component 注解直接或者间接标记的类,继承了@Component的注解包括:@Controller、@RestController、@Repository、@Service、@Configuration等),而 @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);
            }
        }

至此,需要被代理的类被 Spring 全部找到,但是这还远远不够,我们需要引入AOP框架取处理这些类。

如何引入AOP框架?

经过 @ComponentScan 扫描,需要被代理的类 TestAspectJ 和 TestService 被 Spring 找到并注册到 beanDefinitionMap( TestAspectJ 和 TestService 的内容作用机制后面再讲),后面先引入 spring-boot-starter-aop 的组件, 我们用的 Spring boot 版本是 2.4.4 , 接下来就说明下这个组件是如何启用的。

spring-boot-autoconfigure(AopAutoConfiguration)

以前用过 AOP 的都知道, 开启 Spring AOP 需要一个注解 @EnableAspectJAutoProxy,但在启动类 DemoApplication 上并没有发现这个注解,因为 Spring boot 有一个 spring-boot-autoconfigure 模块, autoconfigure 会自动帮我们启用一些组件, 就比如我们现在需要的 aop 模块。

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

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 {

		}

	}
}
@EnableAutoConfiguration(@Import)

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

  • 这个方法处理了 DemoApplication 里面的 @SpringBootApplication -> @EnableAutoConfiguration -> @Import(AutoConfigurationImportSelector.class) 注解,并把 AutoConfigurationImportSelector 对象放进了 deferredImportSelectors list 中。
class ConfigurationClassParser doProcessConfigurationClass

// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
  • 等 DemoApplication 被彻底 parse 完成,在方法返回时,会最后处理 deferredImportSelectors list
class ConfigurationClassParser doProcessConfigurationClass

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 的对象列表。

  • getSpringFactoriesLoaderFactoryClass() 为 EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());

在这里插入图片描述

@Import 导入的类并没有直接被注册到 beanDefinitionMap,等 demoApplication 被 ConfigurationClassParser 解析完毕后,通过 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本身并不重要,重要的在于方法注解 @EnableAspectJAutoProxy→ @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 , 这两者本身无逻辑,但是通过它们引入了核心类
  • AnnotationAwareAspectJAutoProxyCreatorAopAutoConfiguration 顺便也通过 @import 引入了 AspectJAutoProxyRegistrarAspectJAutoProxyRegistrar 又注册了 AnnotationAwareAspectJAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator 负责找出所有 @AspectJ修饰过的对象,为实现 AOP 做准备。

上菜:AOP是如何代理的

先把我本地的demo应用代码放出来。前面说到, 我们已经准备好了各种食材,这一篇真正开始看看 AOP 是怎么代理某个对象的.

@Aspect
@Component
public class TestAspectJ {

    @Pointcut("execution(* com.example.demo.service.TestService.test())")
    public void testPointcut() {
    }

    @Before("testPointcut()")
    public void before() {
        System.out.println("i am coming");
    }

    @After("testPointcut()")
    public void after() {
        System.out.println("i will opt out");
    }
}


public interface TestInterface {

    void test();
}


@Service
public class TestService implements TestInterface {

    @Override
    public void test() {
        System.out.println("i am here~");
    }
}

创建代理对象切入点

postProcessBeforeInstantiation

postProcessBeforeInstantiation 为 InstantiationAwareBeanPostProcessor 定义的方法。触发时机在 对象实例化前调用,AnnotationAwareAspectJAutoProxyCreator 利用这个方法,做了两件事情:

  • 在这里查找所有的 @Aspect 修饰的类.
  • 通过TargetSourceCreator提前生成代理对象
查找所有的 @Aspect 修饰的类

上面的总结也提到了, AnnotationAwareAspectJAutoProxyCreator 利用 postProcessBeforeInstantiation 方法,查找所有的 @Aspect 修饰的类.下面就是具体查找的代码.

当创建整个 Spring应用 第一个对象时, 会走 createBean 方法, 然后经过AnnotationAwareAspectJAutoProxyCreatorpostProcessBeforeInstantiation 方法的时候,开始找所有的 @Aspect 修饰的对象,下面是调用链.

- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
    - org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
        - org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip
            - org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors


//当走到 `findCandidateAdvisors` 时,首先找到所有的 `beanNames` ,然后开始循环遍历. `TestAspectJ` 也是其中之一
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);


//判断这个类是不是有 `@Aspect` 修饰,这时候判断的 beanType 如果是 `TestAspectJ` ,那么就会返回 true
this.advisorFactory.isAspect(beanType)


//确定是 `@Aspect` 对象后,会加入缓存,因为 `findCandidateAdvisors` 后续还会用到,所以第一次已经找到后,后续就直接从缓存中获取,提升效率. 
aspectNames.add(beanName);
this.aspectBeanNames = aspectNames;


//将 `@Aspect` 对象转换为 `Advisor` 对象(Base interface holding AOP advice (action to take at a joinpoint)),因为后续 AOP 逻辑依靠的是 `Advisor`.
MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

//同样的,Advisor对象也会被缓存起来, 以备后用.
this.advisorsCache.put(beanName, classAdvisors);

看到这里,其实在 Spring IOC 阶段, 每个对象执行 postProcessBeforeInstantiation 的时候,再准确的说是 findCandidateAdvisors 方法, 会从 beanFactory 找所有符合的 AOP advice. 这时可能会问,spring 管理的对象那么多,每个都执行一遍是不是太慢了?所以Spring的设计是只有第一次会完整执行逻辑, 随后获取的都是缓存。

提前生成代理对象
  1. 需要提供AbstractBeanFactoryBasedTargetSourceCreator的实现类
  2. 需要提供TargetSource接口的实现。
  3. 需要调用 AbstractAutoProxyCreator#setCustomTargetSourceCreators来设置TargetSourceCreator。
wrapIfNecessary

真正开始代理这个对象有三个入口,上面已经说了一个,还有两个。这两个入口都调用wrapIfNecessary方法。先介绍wrapIfNecessary的大致流程。


    - org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
        - org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
            - org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
                - org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors

上面的调用链有没有发现熟悉的方法? 没错, findCandidateAdvisors方法又出现了, 具体来说我们先从 findEligibleAdvisors 方法看起.

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //直接从缓存 找到所有的 `AOP advice`
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //再看看这个对象能不能跟 candidateAdvisors 匹配上
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        //返回能跟这个对象匹配上的 `AOP advice`
        return eligibleAdvisors;
    }

findEligibleAdvisors 作用就是 筛选出能与当前对象匹配的 AOP advice . 从下图可知,能与TestService 匹配的 advice 正好就是 TestAspectJ 类所写的内容.

在这里插入图片描述

到这一步,AOP 已经到了创建代理对象的步骤:

  1. 新建 ProxyFactory 对象,对象包含了 Advisors待代理的对象 等必要的信息.
  2. createAopProxy 方法确定代理的模式,jdk 动态代理 或者 cglib.
    • 之前设置了 spring.aop.proxy-target-class=false ,所以这里会做判断选择 JdkDynamicAopProxy.
  3. JdkDynamicAopProxy的getProxy方法会调用 Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);.到这里是不是逻辑就很清楚了,Proxy.newProxyInstance做了什么直接看我另一篇博客 从代理模式再出发!Proxy.newProxyInstance的秘密

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!NativeDetector.inNativeImage() &&
                (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
getEarlyBeanReference

在 getBean 最开始,Spring 会先从缓存尝试获取对象,如果这个对象存在循环依赖,那么调用链路会有变化,会通过ObjectFactory调用getEarlyBeanReference 提前暴露对象,对于代理类来说,提前暴露也就意味着代理的过程也要提前,所以这个入口纯粹是为了循环依赖的情况服务的。

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyBeanReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}
postProcessAfterInitialization

postProcessAfterInitialization 触发时机在目标对象初始化之后,且属性已经设置。并且为了避免跟前面getEarlyBeanReference入口重复执行,特意多加了一个if判断。

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyBeanReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

修订记录

2024-12-19 重新修订文章,两篇并成一篇,并调整文章结构。


http://www.kler.cn/a/447749.html

相关文章:

  • MFC/C++学习系列之简单记录13
  • Vue CLI 脚手架创建项目流程详解 (2)
  • 2014年IMO第4题
  • 数智化医院分布式计算框架融合人工智能方向初步实现与能力转换浅析
  • 8位移位寄存器的verilog语言
  • clickhouse-数据库引擎
  • YOLO模型分布式训练:步骤与操作方式
  • 【面经】python后端开发工程师
  • CFD POST导出动画
  • Open3D 进阶(26)Hierarchical Clustering算法实现点云分割
  • 【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
  • 入门靶机:DC-1的渗透测试
  • OpenAI直播发布第11天:ChatGPT桌面客户端升级,就这?
  • 一些经济政治学类书籍推荐 --- 以及与之相关我的经历和理解
  • 前端生成docx文档、excel表格、图片、pdf文件
  • docker 软连接修改存储位置
  • MySQL列类型
  • 知网研学 | 知网文献(CAJ+PDF)批量下载
  • 前端实现图片压缩
  • C++进阶-1-单继承、多继承、虚继承
  • SpringBoot配置Swagger和MybatisPlus
  • memcached 与 redis 的区别?
  • 集成方案 | Docusign + 金蝶云,实现合同签署流程自动化!
  • Ubuntu22.04配置3D gaussian splatting
  • 概率论基础
  • postmam 请求报 Bad Request This combination of host and port requires TLS.解决办法