封面来源:碧蓝航线 深度回音 活动CG

本文涉及的代码: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 注解标记的方法不能是 privatefinal 的,不然怎么增强呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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() 方法呢?难道说会有增强后的方法会有特殊逻辑?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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 容器中的对象吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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模式

  • 当被 @Bean 标记的方法存在于未被 @Configuration 注解标记的类里时,这些方法将以 Lite 模式处理,反之则以 Full 模式处理。
  • 与 Full 模式不同,Lite 模式不能声明 Bean 之间的依赖关系。在 Lite 模式下,@Bean 标记的方法调用其他 @Bean 标记的方法时,后者被认为是一个普通的工厂方法,返回的是一个普通对象,而不是被 Spring 管理的 Bean。除此之外,Lite 模式下不会创建 CGLib 动态代理对象,在类设计方面没有限制(类可以是 final 的,被 @Bean 标记的方法可以是 privatefinal 的)。
  • 通常情况下,@Bean 注解应当与 @Configuration 注解一起使用,确保始终使用 Full 模式,使跨方法的调用能够被重定向到 Spring 容器的生命周期的管理,减少在 Lite 模式下难以定位的 BUG。

2. 源码分析

2.1 提出问题

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

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

2.2 直接上结论

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

ConfigurationClassPostProcessor的类图

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

回顾一下:

  • BeanFactoryPostProcessorBeanFactory 的后置处理器,能够在 BeanFactory 实例化 Bean 之前对 BeanDefinition 进行修改,比如修改 Bean 的属性值、添加额外的属性等等。注意与 BeanPostProcessor 的区别,后者是对已经实例化的 Bean 进行后置处理;
  • BeanDefinitionRegistryPostProcessorBeanDefinitionRegistry 的后置处理器,BeanFactoryPostProcessor 的子接口,与之相比,该接口还能够动态注册新的 BeanDefinition,因此 BeanDefinitionRegistryPostProcessor 相比于 BeanFactoryPostProcessor 会被更早调用。

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

2.3 postProcessBeanDefinitionRegistry

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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() 方法。这个方法比较长,关注前面几行即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,还不能存在工厂方法;
  2. 获取 BeanDefinitionAnnotationMetadata
  3. 通过 AnnotationMetadata 获取当前 BeanDefinition 上的 @Configuration 注解属性,通过判断为 BeanDefinition 设置一些额外属性;
  4. 为满足条件的 BeanDefinition 设置 Order 值。

重点放在第三点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 注解,并且 BeanDefinitionCANDIDATE_ATTRIBUTE 属性值为 true(Spring 6 新增判断,默认情况下就为 true),同时 isConfigurationCandidate() 方法也返回 true,那么就采取 Lite 模式。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 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 模式的配置类进行增强。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
}

看看是如何增强的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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() 实现了增强的主要逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 对象中没有设置 CallbackCallback[],而是设置了 CallbackFilter,这一块可以参考 【设计模式】代理模式 一文,里面介绍了 CGLib 中 CallbackCallbackFilter 的相关内容。

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

1
2
3
4
5
6
7
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 是这样建立的:

1
2
3
4
5
6
7
8
9
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());
}

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

callbacks 共有三种,分别是:

1
2
3
4
5
static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};

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

  • BeanMethodInterceptor 用于匹配被 @Bean 注解标记的方法
  • BeanFactoryAwareMethodInterceptor 用于匹配名为 setBeanFactory 的方法

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@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);
}

核心逻辑如下:

  • 首先判断当前代理是否为作用域代理,此处显然不是;

  • 然后判断获取的 Bean 是否是 FactoryBean,如果是,就去代理 getObject(),执行到 getObject() 方法时,去 Spring 容器中搜索需要的 Bean,此处显然也不是;

  • 接着使用 isCurrentlyInvokedFactoryMethod() 方法判断当前执行的方法是否为工厂方法,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Configuration
    static class FullConfig {
    @Bean
    public Person person() {
    Person person = new Person();
    person.setBook(book());
    return person;
    }

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

    直接调用 book() 方法时,isCurrentlyInvokedFactoryMethod() 方法返回 true,那么将执行 MethodProxy#invokeSuper() 方法,相当于直接调用工厂方法;而在 person() 方法中调用 book() 方法时,则会返回 false,来到最后一步;

  • 执行 resolveBeanReference() 方法处理 Bean 的引用,先去 Spring 容器中查找对应的 Bean,如果存在,则直接使用,反之调用目标方法创建 Bean 并添加到 Spring 容器中。

2.5 getBeanFactory

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

1
2
3
4
5
6
7
8
9
10
11
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 对象时会执行:

1
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}

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

1
public BeanFactory $$beanFactory = null

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

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

1
2
3
4
5
static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};

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

创建 Enhancer 时还会设置 interfaces

1
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
1
2
3
4
5
6
public interface EnhancedConfiguration extends BeanFactoryAware {
}

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

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

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

1
2
3
4
5
6
7
8
9
10
11
@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() 方法进行增强:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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 实例。