SpringBoot中將@Bean方法解析為BeanDefinition詳解
SpringBoot如何將@Bean方法注冊為BeanDefinition
我們以MybatisPlusAutoConfiguration為例說明sqlSessionFactory()這個方法如何解析為BeanDefinition。
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//...
} 這個解析過程是在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForBeanMethod方法。
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
//本文這里是sqlSessionFactory
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
//判斷是否需要跳過
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
//判斷是否需要跳過
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
//獲取到@Bean的注解信息
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// Consider name and any aliases
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
//嘗試獲取bean的名稱,默認使用methodName
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden
//嘗試注冊別名
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
//校驗BeanDefinition是否存在、是否允許覆蓋
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
//得到一個ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
//方法是否為靜態(tài)方法
if (metadata.isStatic()) {
// static @Bean method
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
}
else {
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
}
beanDef.setUniqueFactoryMethodName(methodName);
}
else {
//實例方法,非靜態(tài)方法
// instance @Bean method
//設(shè)置FactoryBeanName,本文這里是
//com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
beanDef.setFactoryBeanName(configClass.getBeanName());
// 設(shè)置factoryMethodName 本文這里是sqlSessionFactory
beanDef.setUniqueFactoryMethodName(methodName);
}
//本文這里跳過 這里是SimpleMethodMetadata
if (metadata instanceof StandardMethodMetadata) {
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
}
//設(shè)置注入標識 AUTOWIRE_CONSTRUCTOR=3
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
//設(shè)置SKIP_REQUIRED_CHECK_ATTRIBUTE屬性
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
//BeanDefinition的通用后置處理,如Lazy、Primary、DependsOn、Role以及Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
//解析注解上配置的autowire
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
//解析注解上配置的autowireCandidate
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
//解析注解上配置的initMethod
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
//解析注解上配置的destroyMethod
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
//獲取@Scope注解信息
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getString("value"));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
//嘗試創(chuàng)建代理
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
//注冊BeanDefinition,beanName默認是methodName sqlSessionFactory
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}方法流程如上所示,核心步驟都加了注釋。這里要特別注意的是如下幾點:
- 得到的BeanDefinition是ConfigurationClassBeanDefinition類型;
- 會為BeanDefinition設(shè)置factoryMethodName,這意味著當實例化這個bean的時候?qū)⒉捎霉S方法;
- 會區(qū)分方法是否為靜態(tài)方法來設(shè)置BeanClass或者FactoryBeanName
這里得到的configClass

這里得到的MethodMetadata

最終得到的BeanDefinition屬性
annotationMetadata = {SimpleAnnotationMetadata@5352}
factoryMethodMetadata = {SimpleMethodMetadata@5635}
decoratedDefinition = null
qualifiedElement = null
stale = false
allowCaching = true
isFactoryMethodUnique = true
targetType = null
resolvedTargetType = null
isFactoryBean = null
factoryMethodReturnType = null
factoryMethodToIntrospect = null
constructorArgumentLock = {Object@5823}
resolvedConstructorOrFactoryMethod = null
constructorArgumentsResolved = false
resolvedConstructorArguments = null
preparedConstructorArguments = null
postProcessingLock = {Object@5824}
postProcessed = false
beforeInstantiationResolved = null
externallyManagedConfigMembers = null
externallyManagedInitMethods = null
externallyManagedDestroyMethods = null
beanClass = null
scope = ""
abstractFlag = false
lazyInit = null
autowireMode = 3
dependencyCheck = 0
dependsOn = null
autowireCandidate = true
primary = false
qualifiers = {LinkedHashMap@5825} size = 0
instanceSupplier = null
nonPublicAccessAllowed = true
lenientConstructorResolution = false
factoryBeanName = "com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration"
factoryMethodName = "sqlSessionFactory"
constructorArgumentValues = null
propertyValues = null
methodOverrides = {MethodOverrides@5826}
initMethodName = null
destroyMethodName = "(inferred)"
enforceInitMethod = true
enforceDestroyMethod = true
synthetic = false
role = 0
description = null
resource = {ClassPathResource@5353} "class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]"
source = {SimpleMethodMetadata@5635}
attributes = {LinkedHashMap@5827} size = 1到此這篇關(guān)于SpringBoot中將@Bean方法解析為BeanDefinition詳解的文章就介紹到這了,更多相關(guān)@Bean方法解析為BeanDefinition內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java通過Excel批量上傳數(shù)據(jù)的實現(xiàn)示例
Excel批量上傳是常見的一種功能,本文就來介紹一下java通過Excel批量上傳數(shù)據(jù)的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下2023-10-10
SpringBoot實現(xiàn)Server-Sent Events(SSE)的使用完整指南
使用SpringBoot實現(xiàn)Server-Sent Events(SSE)可以有效處理實時數(shù)據(jù)推送需求,具有單向通信、輕量級和高實時性等優(yōu)勢,本文詳細介紹了在SpringBoot中創(chuàng)建SSE端點的步驟,并通過代碼示例展示了客戶端如何接收數(shù)據(jù),適用于實時通知、數(shù)據(jù)展示和在線聊天等場景2024-09-09
基于SpringBoot實現(xiàn)郵箱找回密碼的代碼示例
本文主要介紹了如何基于SpringBoot實現(xiàn)郵箱找回密碼,文中通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-02-02
MyBatis自定義映射關(guān)系和關(guān)聯(lián)查詢實現(xiàn)方法詳解
這篇文章主要介紹了MyBatis自定義映射關(guān)系和關(guān)聯(lián)查詢實現(xiàn)方法,當POJO屬性名與數(shù)據(jù)庫列名不一致時,需要自定義實體類和結(jié)果集的映射關(guān)系,在MyBatis注解開發(fā)中,使用@Results定義并使用自定義映射,使用 @ResultMap使用自定義映射2023-04-04
淺談Java內(nèi)部類——靜態(tài)內(nèi)部類
這篇文章主要介紹了Java靜態(tài)內(nèi)部類的相關(guān)資料,幫助大家更好的理解和學習Java內(nèi)部類的相關(guān)知識,感興趣的朋友可以了解下2020-08-08
java讀取文件:char的ASCII碼值=65279,顯示是一個空字符的解決
這篇文章主要介紹了java讀取文件:char的ASCII碼值=65279,顯示是一個空字符的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

