Spring是怎么实现@Bean注解的

案例说明

在 Spring 中,我们通过在配置类中使用 @Bean 注解是能成功将其标注的方法返回的 Bean 对象注入到 Spring 容器中。但是如果我们通过容器获取到配置类的对象,在调用配置类对象被 @Bean 修饰的方法是否会重新生成对象呢?代码如下:

MyApplicationContext

1
2
3
4
5
6
7
8
9
public class MyApplicationContext {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyConfiguration myConfiguration = context.getBean(MyConfiguration.class);
Cat cat1 = myConfiguration.getCat();
Cat cat2 = myConfiguration.getCat();
System.out.println(cat1 == cat2);
}
}

MyConfiguration

1
2
3
4
5
6
7
8
9
@Configuration
public class MyConfiguration {
@Bean
public Cat getCat(){
Cat cat = new Cat();
cat.setName("zs");
return cat;
}
}

Cat

1
2
3
4
5
6
7
8
9
10
11
public class Cat {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

实验结果为 true,表明调用代理对象 myConfiguration.getCat() 并不会重新生成新的对象,这显然与我们的认知截然不同。而这一次我们将探究 Spring 是如何实现这一功能的。

Cat 的 BeanDefinition 进入 BeanFactory 的时机

这个过程发生在类名为 AbstractApplicationContext 的核心代码 refresh()invokeBeanFactoryPostProcessors(beanFactory) 方法中。该方法会遍历 BeanFactory 中所有实现了 BeanDefinitionRegistryPostProcessor 接口的对象,调用该对象实现了接口的方法 ***postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)***,而 ConfigurationClassPostProcessor 便实现了这个接口。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
...
@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);
}

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));
}
}
...

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 解析配置类
parser.parse(candidates);
parser.validate();
// 通过解析器获取以及解析过的配置类的信息 configClasses
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
...
// 这一步才是将解析到被@Bean修饰的方法创建成BeanDefinition并添加进BeanFactory
this.reader.loadBeanDefinitions(configClasses);
}
}

这里会创建一个配置文件的解析器 ConfigurationClassParser,通过该解析器的 parser.parse(candidates) 进行解析。该方法经过一系列调用,最终真正干活的是 ***doProcessConfigurationClass(configClass, sourceClass, filter)***,它会解析标注在配置类上 @PropertySource、@ComponentScan、 @Import、@ImportResource 和 @Bean 的标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
...
// Process any @PropertySource annotations
...
// Process any @ComponentScan annotations
...
// Process any @Import annotations
...
// Process any @ImportResource annotations
...
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

}

这里做的工作只是将配置类上标注了 @Bean 的方法包装成 MethodMetadata 并添加进配置类的包装对象 configClass 中。当解析器 ConfigurationClassParser 解析完成之后,ConfigurationClassPostProcessorConfigurationClassPostProcessor(…) 方法会从解析器中拿到解析过的配置文件的包装对象 configClass,将对应的方法分装为 ConfigurationClassBeanDefinition 并注册进 BeanFactory。

Car 对象的生成时机

同样发送在 refresh() 方法中,在其 finishBeanFactoryInitialization(beanFactory) 方法里会遍历容器中所有的注册了的BeanDefinitionNames,并使用 getBean(beanName) 创建实例。

在经过 getBean -> doGetBean -> createBean -> doCreateBean 的一系列方法调用后,会进入 createBeanInstance 方法,在该方法中,会判断出传入的 BeanDefinition 包含一个工厂方法,则会使用工厂方法的方式创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);

if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}

// 使用Supplier的方式创建对象
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}

// 使用工厂方法FactoryMethod的方式创建对象
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 使用构成方法创建对象
...
}

之后在经过一系列的方法调用,最终会获取到 getCar() 的 Method 对象和 MyConfiguration 类的实例,通过反射的方式创建 Object result = factoryMethod.invoke(factoryBean, args)

为何多次调用 myConfiguration.getCat() 方法会返回同一对象

Spring 通过动态代理的方式对配置类 MyConfiguration 进行了增强。当调用 getCar() 方法后会进入 ConfigurationClassEnhancer 的内部子类 BeanMethodInterceptorintercept 方法中,该方法最终通过获取到 BeanFactory 并调用 getBean(beanName) 的方法获取对象。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {

ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}

// 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.
// 如果要生成对象的类是 FactoryBean时,走以下逻辑
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}

// 如果该对象正在创建,则调用被代理对象的方法创建实例
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 这方法最终还是调用 beanFactory.getBean(beanName)
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

MyConfiguration类增强的时机

发送在 refresh()invokeBeanFactoryPostProcessors(beanFactory) 方法中,该方法会遍历实现了 BeanFactoryPostProcessor 的子类并调用其 postProcessBeanFactory 方法。而 ConfigurationClassPostProcessor 便实现了这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
// 如果进入该判断,表明 BDRPP 的回调方法没用被调用,则先调用该方法解析配置类,也就是解析 @Bean、@ComponentScan、@Import等注解
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));
}

继续 Debug 代码,最终发现他会调用 ConfigurationClassEnhancer 类中的 newEnhancer 方法完成代理类的增强。

1
2
3
4
5
6
7
8
9
10
11
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}