跳到正文
返回

@Configuration 注解的那些事

发表于 更新于
浏览量: --
系列文章: SpringBoot 源码剖析 3 / 8
  1. 从回调到 LambdaSafe
  2. 获取 ApplicationContext
  3. @Configuration 注解的那些事 当前文章
  4. SpringBoot 常用拓展点
  5. SpringBoot Shutdown Hook
  6. Spring 的事件监听机制
  7. BeanDefinition
  8. Spring 配置类的解析

本文涉及的代码:springboot-study/FullLiteModeTest.java

本文基于 SpringBoot 3.1.x

0. 前言

本文于 2020-06-07 初创,原名为《SpringBoot 配置类》。在学习 SpringBoot 初期,以一次水群为背景,介绍了 Spring 中 @Component、@Bean 和 @Component 的关系、区别、使用,以及 SpringBoot 配置类中 @Bean 的使用。

三年过后,虽长进不多,但回看当初的文章,字里行间尽显稚气,甚至带有误人子弟之嫌。尽管这文章没有任何传播性,但万一在未来被某些初学者瞧见,而奉为圭臬,那自己岂不是他人成长路上的绊脚石,遗臭万年?

算了,不往自己脸上贴金了,主要还是因为当初文章内容漏洞百出,为了避免在未来被人贻笑大方,还是重构一番为妙。

本次重构后将不再介绍那些基础概念与使用,将从官方文档出发,从源码层面介绍 @Configuration 注解的 Full 模式与 Lite 模式。

1. Full 模式与 Lite 模式

1.1 Full 模式

@Configuration 注解与 @Bean 注解联合使用,且 不添加任何额外属性 时,即为 Full 模式。

在 Full 模式情况下,会为配置类生成一个代理对象,所有被 @Bean 注解标记的方法会被增强。这种情况下,配置类不能是 final 的,被 @Bean 注解标记的方法不能是 private 或 final 的,不然怎么增强呢?

@Setter
@Getter
static class Person {
    private Book book;
    private Book anotherBook;
}

static class Book {
}

@Configuration
static class FullConfig {
    @Bean
    public Person person() {
        Person person = new Person();
        person.setBook(book());
        return person;
    }

    @Bean
    public Book book() {
        return new Book();
    }
}

为什么要在 person() 方法中调用 book() 方法呢?难道说会有增强后的方法会有特殊逻辑?

private static final String LABEL_VALUE = "$$SpringCGLIB$$";

@Test
@SneakyThrows
public void testFullConfig() {
    ApplicationContext context = new AnnotationConfigApplicationContext(FullConfig.class);
    FullConfig config = context.getBean(FullConfig.class);
    // 生成的是 config 代理对象
    assertThat(config.getClass().getName()).contains(LABEL_VALUE);

    // person 里的 book 和 Spring 容器中的是同一个
    Book bookInSpringContainer = (Book) context.getBean("book");
    Book bookInPerson = ((Person) (context.getBean("person"))).getBook();
    assertThat(bookInPerson).isSameAs(bookInSpringContainer);
}

Full 模式下会为配置类生成代理对象,代理对象的 className 中包含 $SpringCGLIB$ 字符串,表明该对象由 Spring CGLib 生成。

在 person() 方法中调用 book() 方法获取到的 Book 对象与 Spring 容器中的 Book 对象是同一个。也就是说在调用 book() 方法时会去 Spring 容器中查看是否存在 Book 对象,如果已经存在,那么直接使用该对象,而不会再去执行 book() 方法创建新的 Book 对象,如果 Spring 容器中不存在 Book 对象,才会创建新的 Book 对象。

1.2 Lite 模式

Lite 模式,即精简模式。移除 @Configuration 注解,取而代之的是 @Component 注解,或者使用 @ComponentScan、@Import 等注解将类扫描、导入进 Spring 容器,最终都将采用 Lite 模式。

@Component
static class LiteConfig {

    @Autowired
    @Qualifier("liteBook")
    private Book book;

    @Bean
    public Person litePerson() {
        Person person = new Person();
        // 在 IDEA 中这样调用时会出现 error 提示: 
        // Method annotated with @Bean is called directly. Use dependency injection instead. 
        person.setBook(liteBook());
        // 也可以通过方法参数传进来
        person.setAnotherBook(book);
        return person;
    }

    @Bean
    public Book liteBook() {
        return new Book();
    }
}

在 Lite 模式下还会生成代理对象吗?在当前方法中调用另一个被 @Bean 注解标记的方法获取到的是 Spring 容器中的对象吗?

@Test
public void testLiteConfig() {
    ApplicationContext context = new AnnotationConfigApplicationContext(LiteConfig.class);
    LiteConfig config = context.getBean(LiteConfig.class);
    // 生成的是原始对象
    assertThat(config.getClass().getName())
            .doesNotContain(LABEL_VALUE)
            .isEqualTo(LiteConfig.class.getName());

    // person 中的 book 与 Spring 容器中的不是同一个
    Person person = (Person) context.getBean("litePerson");
    Book liteBook = (Book) context.getBean("liteBook");
    assertThat(person.getBook()).isNotSameAs(liteBook);
    assertThat(person.getAnotherBook()).isSameAs(liteBook);
}

在 Lite 模式下不会生成代理对象,在当前方法中调用另一个被 @Bean 注解标记的方法获取到的不再是 Spring 容器中的对象,此时被 @Bean 注解标记的方法只是一个普通的工厂方法。

由于不再生成代理对象,对于类或方法的设计不再有要求,它们可以是 final 的,方法还可以是 private 的。

如果要确保 Person 对象中的 Book 对象与 Spring 容器中的是同一个,可以通过方法参数将 Book 传进来,也可以使用 @Autowired 注解注入 Book 对象,然后使用。

除此之外,如果在使用了 @Configuration 注解的基础上设置 proxyBeanMethods 属性(意为是否代理 @Bean 方法)为 false 时,最终也会使用 Lite 模式。

1.3 小结

在官方文档 Basic Concepts: @Bean and @Configuration 中对 @Configuration 注解的 Full 模式与 Lite 模式有以下介绍:

Full模式与Lite模式

2. 源码分析

2.1 提出问题

被 @Configuration 注解标记的类也会被 Spring 托管,但这个 Bean 并不是原始对象,而是代理对象,这也是与 @Component 注解及其衍生注解的区别。

那为什么会为被 @Configuration 注解标记的类生成代理对象呢?通过代理又增强了哪些功能呢?

2.2 直接上结论

Spring 配置类中的注解由 ConfigurationClassPostProcessor 后置处理器解析。

ConfigurationClassPostProcessor的类图

ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,如果对它不是很熟悉,那么其父接口 BeanFactoryPostProcessor 一定是个熟面孔。

回顾一下:

BeanFactoryPostProcessor 中定义的方法是 postProcessBeanFactory(),BeanDefinitionRegistryPostProcessor 中定义的方法是 postProcessBeanDefinitionRegistry(),后者相比于前者更早被调用。

2.3 postProcessBeanDefinitionRegistry

ConfigurationClassPostProcessor 中的 postProcessBeanDefinitionRegistry() 方法做了些什么呢?

private final Set<Integer> registriesPostProcessed = new HashSet<>();

private final Set<Integer> factoriesPostProcessed = new HashSet<>();

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

多个 contains() 方法的判断是为了保证该方法只被执行一次,重点在最后调用的 processConfigBeanDefinitions() 方法。这个方法比较长,关注前面几行即可:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    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);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

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

    // --snip--
}

一种做了三件事:

  1. 遍历所有 BeanDefinition;
  2. 对每个 BeanDefinition 进行判断,分析其是否为候选项,如果是就添加到 configCandidates 中;
  3. configCandidates 为空时,结束方法。

对每个 BeanDefinition 进行了两种判断:

  1. 首先判断是否存在 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE 属性,该属性记录了配置类将采取 Lite 模式还是 Full 模式,首次处理时不存在该属性,会进入第二种判断;
  2. 使用 ConfigurationClassUtils.checkConfigurationClassCandidate() 方法判断 BeanDefinition 是否为配置类,并对其进行标记。

ConfigurationClassUtils.checkConfigurationClassCandidate() 方法也贼长,一共做了四件事:

  1. 基础的判断:BeanDefinition 对应的 className 不能为 null,创建 BeanDefinition 的工厂方法也不能为 null(也就是这个 BeanDefinition 必须是由工厂方法创建的);
  2. 获取 BeanDefinition 的 AnnotationMetadata;
  3. 通过 AnnotationMetadata 获取当前 BeanDefinition 上的 @Configuration 注解属性,通过判断为 BeanDefinition 设置一些额外属性;
  4. 为满足条件的 BeanDefinition 设置 Order 值。

重点放在第三点:

static boolean checkConfigurationClassCandidate(
    BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

    // --snip--

    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
        // 还记得在 processConfigBeanDefinitions() 中判断了 CONFIGURATION_CLASS_ATTRIBUTE 是否存在吗?
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    else if (config != null || Boolean.TRUE.equals(beanDef.getAttribute(CANDIDATE_ATTRIBUTE)) ||
             isConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }

    // --snip--
}

第三点主要用于判断配置类将采取那种模式:

  1. 如果存在 @Configuration 注解,并且其 proxyBeanMethods 属性值不为 false,那么采取 Full 模式;
  2. 如果存在 @Configuration 注解,并且 BeanDefinition 的 CANDIDATE_ATTRIBUTE 属性值为 true(Spring 6 新增判断,默认情况下就为 true),同时 isConfigurationCandidate() 方法也返回 true,那么就采取 Lite 模式。

又进入 isConfigurationCandidate() 方法吧,这个方法倒比较短:

private static final Set<String> candidateIndicators = Set.of(
    Component.class.getName(),
    ComponentScan.class.getName(),
    Import.class.getName(),
    ImportResource.class.getName()
);

static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
        return false;
    }

    // Any of the typical annotations found?
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }

    // Finally, let's look for @Bean methods...
    return hasBeanMethods(metadata);
}

一共三种判断:

  1. 不考虑接口和注解
  2. 是否存在典型注解,存在的情况下直接返回 true,也就是将采用 Lite 模式;
  3. 配置类中是否存在被 @Bean 注解标记的方法,如果存在,也采用 Lite 模式。

到此,配置类对应的 BeanDefinition 已经被标记了将要采取 Full 模式还是 Lite 模式。

2.4 postProcessBeanFactory

再来看 postProcessBeanFactory() 方法:

/**
 * Prepare the Configuration classes for servicing bean requests at runtime
 * by replacing them with CGLIB-enhanced subclasses.
 */
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    int factoryId = System.identityHashCode(beanFactory);
    // 确保当前方法只执行一次
    if (this.factoriesPostProcessed.contains(factoryId)) {
        throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
    }
    this.factoriesPostProcessed.add(factoryId);
    // 还记得在 postProcessBeanDefinitionRegistry() 方法中的判断吗?
    if (!this.registriesPostProcessed.contains(factoryId)) {
        // BeanDefinitionRegistryPostProcessor hook apparently not supported...
        // Simply call processConfigurationClasses lazily at this point then.
        processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
    }

    enhanceConfigurationClasses(beanFactory);
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

方法注释表明了该方法的作用: 通过将配置类替换为 CGLIB 增强的子类,准备在运行时为 Bean 请求提供服务的配置类。

一共做了四件事:

  1. 与 postProcessBeanDefinitionRegistry() 一样,确保当前方法只被执行一次;
  2. 判断是否已经执行过 postProcessBeanDefinitionRegistry() 方法,如果没执行过就调用 processConfigBeanDefinitions() 方法,该方法也是 postProcessBeanDefinitionRegistry() 的核心;
  3. 执行 enhanceConfigurationClasses() 方法;
  4. 为 BeanFactory 添加一个 Bean 后置处理器 ImportAwareBeanPostProcessor;

重点放在第三点——对配置类进行增强,这个方法也比较长,但概括下其实就做了两件事:

  1. 遍历 BeanDefinition,找到采用 Full 模式的配置类对应的 BeanDefinition,将它们放到 configBeanDefs 中;
  2. 遍历 configBeanDefs 对采用 Full 模式的配置类进行增强。
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        // 对应第一点
    }
    if (configBeanDefs.isEmpty()) {
        // nothing to enhance -> return immediately
        enhanceConfigClasses.end();
        return;
    }

    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
        // 对应第二点
    }
    enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

看看是如何增强的:

ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
   AbstractBeanDefinition beanDef = entry.getValue();
   // If a @Configuration class gets proxied, always proxy the target class
   beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
   // Set enhanced subclass of the user-specified bean class
   Class<?> configClass = beanDef.getBeanClass();
   Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
   if (configClass != enhancedClass) {
      if (logger.isTraceEnabled()) {
         logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
               "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
      }
      beanDef.setBeanClass(enhancedClass);
   }
}

并不会直接生成代理对象,而是获取到增强后的 Class 对象。 这很好理解,现在是后置处理 BeanDefinition 的阶段,如果直接生成代理对象要放在哪里呢?因此只能先获取到增强后的 Class 对象,后续在实例化 Bean 时根据这个 Class 对象生成代理对象。

ConfigurationClassEnhancer#enhance() 实现了增强的主要逻辑:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
   if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
      if (logger.isDebugEnabled()) {
         logger.debug(String.format("Ignoring request to enhance %s as it has " +
               "already been enhanced. This usually indicates that more than one " +
               "ConfigurationClassPostProcessor has been registered (e.g. via " +
               "<context:annotation-config>). This is harmless, but you may " +
               "want check your configuration and remove one CCPP if possible",
               configClass.getName()));
      }
      return configClass;
   }
   Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
   if (logger.isTraceEnabled()) {
      logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
            configClass.getName(), enhancedClass.getName()));
   }
   return enhancedClass;
}

其中的重点在 newEnhancer() 和 createClass() 方法。

newEnhancer() 方法

顾名思义,用于 new 一个 Enhancer 对象:

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass);
    // 相当于让代理类实现 BeanFactoryAware 接口,后续会对 setBeanFactory() 方法也进行增强
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    // Spring 生成的 CGLib 代理类的 className 带有 $$SpringCGLIB$$ 就是因为设置了此 NamingPolicy
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setAttemptLoad(true);
    // 用于设置一个 `public BeanFactory $$beanFactory = null` 的字段
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

创建的 Enhancer 对象中没有设置 Callback 或 Callback[],而是设置了 CallbackFilter,这一块可以参考 【设计模式】代理模式 一文,里面介绍了 CGLib 中 Callback 和 CallbackFilter 的相关内容。

这里设置的 CallbackFilter 是一个常量:

static final Callback[] CALLBACKS = new Callback[] {
    new BeanMethodInterceptor(),
    new BeanFactoryAwareMethodInterceptor(),
    NoOp.INSTANCE
};

private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

利用 CallbackFilter#accept() 方法能够建立起与 Callback[] 的映射关系,在 ConditionalCallbackFilter 是这样建立的:

public int accept(Method method) {
   for (int i = 0; i < this.callbacks.length; i++) {
      Callback callback = this.callbacks[i];
      if (!(callback instanceof ConditionalCallback conditional) || conditional.isMatch(method)) {
         return i;
      }
   }
   throw new IllegalStateException("No callback available for method " + method.getName());
}

Callback 是 ConditionalCallback 的情况下,使用其 isMatch() 方法进行匹配;否则直接返回索引。

callbacks 共有三种,分别是:

static final Callback[] CALLBACKS = new Callback[] {
      new BeanMethodInterceptor(),
      new BeanFactoryAwareMethodInterceptor(),
      NoOp.INSTANCE
};

前两种均为 ConditionalCallback 实例,根据它们对 isMatch() 方法的重写可知:

因此 BeanMethodInterceptor 是此处需要关注的:

@Override
public boolean isMatch(Method candidateMethod) {
   return (candidateMethod.getDeclaringClass() != Object.class &&
         !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
         BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}

BeanMethodInterceptor 还实现了 MethodInterceptor 接口,这接口就很熟悉了,是 CGLib 中的一种 Callback,实现对目标方法的增强。也就是在 Full 模式的情况下,调用 @Bean 注解标记的方法时,调用的其实是重写的 intercept() 方法:

@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, 
                        Object[] beanMethodArgs,
                        MethodProxy cglibMethodProxy) throws Throwable {
    // 通过代理对象获取 BeanFactory,这是怎么做到的?
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // Determine whether this bean is a scoped-proxy
    if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
        // --snip--
    }

    // To handle the case of an inter-bean method reference, we must explicitly check the
    // container for already cached instances.

    // First, check to see if the requested bean is a FactoryBean. If so, create a subclass
    // proxy that intercepts calls to getObject() and returns any cached bean instance.
    // This ensures that the semantics of calling a FactoryBean from within @Bean methods
    // is the same as that of referring to a FactoryBean within XML. See SPR-6602.
    if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
        factoryContainsBean(beanFactory, beanName)) {
        // --snip--
    }

    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // --snip--
    }

    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

核心逻辑如下:

2.5 getBeanFactory

getBeanFactory() 是 BeanMethodInterceptor#intercept() 方法中第一行调用的方法,通过传入的代理对象获取 BeanFactory 实例,这是怎么做到的呢?

private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
    // 获取代理对象中 $$beanFactory 字段
    Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
    Assert.state(field != null, "Unable to find generated bean factory field");
    // 获取该字段值
    Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
    Assert.state(beanFactory != null, "BeanFactory has not been injected into @Configuration class");
    Assert.state(beanFactory instanceof ConfigurableBeanFactory,
                 "Injected BeanFactory is not a ConfigurableBeanFactory");
    return (ConfigurableBeanFactory) beanFactory;
}

$beanFactory 字段类型是 BeanFactory,在创建 Enhancer 对象时会执行:

enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
private static class BeanFactoryAwareGeneratorStrategy extends ClassLoaderAwareGeneratorStrategy {

    public BeanFactoryAwareGeneratorStrategy(@Nullable ClassLoader classLoader) {
        super(classLoader);
    }

    @Override
    protected ClassGenerator transform(ClassGenerator cg) throws Exception {
        ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
            @Override
            public void end_class() {
                // 增加字段的逻辑
                declare_field(Opcodes.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
                super.end_class();
            }
        };
        return new TransformingClassGenerator(cg, transformer);
    }
}

后续会为代理对象增加如下形式的字段:

public BeanFactory $$beanFactory = null

但这个字段的默认值是 null,是什么时候设置的呢?

前文创建 Enhancer 时使用到了三个 Callback:

static final Callback[] CALLBACKS = new Callback[] {
      new BeanMethodInterceptor(),
      new BeanFactoryAwareMethodInterceptor(),
      NoOp.INSTANCE
};

其中的 BeanFactoryAwareMethodInterceptor 将用于对 $beanFactory 字段设置值。

创建 Enhancer 时还会设置 interfaces:

enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
public interface EnhancedConfiguration extends BeanFactoryAware {
}

public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

这使得代理类实现了 BeanFactoryAware 接口,使得能在 Spring 生命周期中拿到 BeanFactory 实例。

回到 BeanFactoryAwareMethodInterceptor 中,它会匹配 setBeanFactory() 方法:

@Override
public boolean isMatch(Method candidateMethod) {
   return isSetBeanFactory(candidateMethod);
}

public static boolean isSetBeanFactory(Method candidateMethod) {
   return (candidateMethod.getName().equals("setBeanFactory") &&
         candidateMethod.getParameterCount() == 1 &&
         BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
         BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
}

然后对 setBeanFactory() 方法进行增强:

@Override
@Nullable
public Object intercept(Object obj, Method method, 
                        Object[] args, MethodProxy proxy) throws Throwable {
    // 获取 $$beanFactory 字段
    Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
    Assert.state(field != null, "Unable to find generated BeanFactory field");
    // 为 $$beanFactory 字段赋值为 setBeanFactory() 方法的第一个参数
    field.set(obj, args[0]);

    // Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
    // If so, call its setBeanFactory() method. If not, just exit.
    if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
        return proxy.invokeSuper(obj, args);
    }
    return null;
}

增强后获取到 $beanFactory 字段,并为该字段赋值为 setBeanFactory() 方法的第一个参数。

最终使得通过代理对象能够获取到 BeanFactory 实例。

如果这篇文章对你有帮助,可以通过 赞赏 请我喝杯 Coffee ☕


上一篇
SpringBoot 常用拓展点
下一篇
获取 ApplicationContext