封面来源:碧蓝航线 深度回音 活动 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 注解标记的方法不能是 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 模式有以下介绍:

- 当被
@Bean标记的方法存在于未被@Configuration注解标记的类里时,这些方法将以 Lite 模式处理,反之则以 Full 模式处理 - 与 Full 模式不同,Lite 模式不能声明 Bean 之间的依赖关系。在 Lite 模式下,
@Bean标记的方法调用其他@Bean标记的方法时,后者被认为是一个普通的工厂方法,返回的是一个普通对象,而不是被 Spring 管理的 Bean。除此之外,Lite 模式下不会创建 CGLib 动态代理对象,在类设计方面没有限制(类可以是final的,被@Bean标记的方法可以是private或final的) - 通常情况下,
@Bean注解应当与@Configuration注解一起使用,确保始终使用 Full 模式,使跨方法的调用能够被重定向到 Spring 容器的生命周期的管理,减少在 Lite 模式下难以定位的 BUG
2. 源码分析
2.1 提出问题
被 @Configuration 注解标记的类也会被 Spring 托管,但这个 Bean 并不是原始对象,而是代理对象,这也是与 @Component 注解及其衍生注解的区别。
那为什么会为被 @Configuration 注解标记的类生成代理对象呢?通过代理又增强了哪些功能呢?
2.2 直接上结论
Spring 配置类中的注解由 ConfigurationClassPostProcessor 后置处理器解析。

ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,如果对它不是很熟悉,那么其父接口 BeanFactoryPostProcessor 一定是个熟面孔。
回顾一下:
BeanFactoryPostProcessor:BeanFactory的后置处理器,能够在BeanFactory实例化 Bean 之前对BeanDefinition进行修改,比如修改 Bean 的属性值、添加额外的属性等等。注意与BeanPostProcessor的区别,后者是对已经实例化的 Bean 进行后置处理BeanDefinitionRegistryPostProcessor:BeanDefinitionRegistry的后置处理器,BeanFactoryPostProcessor的子接口,与之相比,该接口还能够动态注册新的BeanDefinition,因此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--
}
一种做了三件事:
- 遍历所有
BeanDefinition; - 对每个
BeanDefinition进行判断,分析其是否为候选项,如果是就添加到configCandidates中; configCandidates为空时,结束方法。
对每个 BeanDefinition 进行了两种判断:
- 首先判断是否存在
ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE属性,该属性记录了配置类将采取 Lite 模式还是 Full 模式,首次处理时不存在该属性,会进入第二种判断; - 使用
ConfigurationClassUtils.checkConfigurationClassCandidate()方法判断BeanDefinition是否为配置类,并对其进行标记。
ConfigurationClassUtils.checkConfigurationClassCandidate() 方法也贼长,一共做了四件事:
- 基础的判断:
BeanDefinition对应的className不能为null,创建BeanDefinition的工厂方法也不能为null(也就是这个BeanDefinition必须是由工厂方法创建的); - 获取
BeanDefinition的AnnotationMetadata; - 通过
AnnotationMetadata获取当前BeanDefinition上的@Configuration注解属性,通过判断为BeanDefinition设置一些额外属性; - 为满足条件的
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--
}
第三点主要用于判断配置类将采取那种模式:
- 如果存在
@Configuration注解,并且其proxyBeanMethods属性值不为false,那么采取 Full 模式; - 如果存在
@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);
}
一共三种判断:
- 不考虑接口和注解
- 是否存在典型注解,存在的情况下直接返回
true,也就是将采用 Lite 模式; - 配置类中是否存在被
@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 请求提供服务的配置类。
一共做了四件事:
- 与
postProcessBeanDefinitionRegistry()一样,确保当前方法只被执行一次; - 判断是否已经执行过
postProcessBeanDefinitionRegistry()方法,如果没执行过就调用processConfigBeanDefinitions()方法,该方法也是postProcessBeanDefinitionRegistry()的核心; - 执行
enhanceConfigurationClasses()方法; - 为
BeanFactory添加一个 Bean 后置处理器ImportAwareBeanPostProcessor;
重点放在第三点——对配置类进行增强,这个方法也比较长,但概括下其实就做了两件事:
- 遍历
BeanDefinition,找到采用 Full 模式的配置类对应的BeanDefinition,将它们放到configBeanDefs中; - 遍历
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用于匹配被@Bean注解标记的方法BeanFactoryAwareMethodInterceptor用于匹配名为setBeanFactory的方法
因此 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);
}
核心逻辑如下:
-
首先判断当前代理是否为作用域代理,此处显然不是
-
然后判断获取的 Bean 是否是
FactoryBean,如果是,就去代理getObject(),执行到getObject()方法时,去 Spring 容器中搜索需要的 Bean,此处显然也不是 -
接着使用
isCurrentlyInvokedFactoryMethod()方法判断当前执行的方法是否为工厂方法,比如:@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 实例,这是怎么做到的呢?
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 实例。