新聞中心
大家好,我是冰河~~

「本章難度」:
「本章重點」:進一步了解@Bean注解的使用方法和如何避免踩坑,并在源碼級別徹底理解和吃透@Bean注解的執(zhí)行流程。
一、學(xué)習(xí)指引
@Bean注解的實現(xiàn)其實沒你想象的那么簡單!
翻看Spring的源碼時,發(fā)現(xiàn)@Bean注解的源碼上標(biāo)注了Since: 3.0,也就是說,@Bean注解是Spring從3.0版本開始提供的源碼。@Bean注解可以標(biāo)注在方法上,將當(dāng)前方法的返回值注入到IOC容器中,也可以標(biāo)注到注解上,作為元注解使用。
還是那句話:如果只想做CRUD程序員,對于@Bean注解了解到這里就已經(jīng)可以了,如果想進一步突破自己,讓自己的技術(shù)能力更上一層樓,則繼續(xù)往下看。
二、注解說明
關(guān)于@Bean注解的一點點說明~~
@Bean注解可以說是使用Spring開發(fā)應(yīng)用程序時,使用的比較多的一個注解,尤其是基于SpringBoot開發(fā)應(yīng)用程序時,@Bean注解使用的就很多了。@Bean注解可以標(biāo)注到方法上,將當(dāng)前方法的返回值注入到IOC容器中。@Bean注解也可以標(biāo)注到注解上,作為元注解使用。
2.1 注解源碼
本節(jié),主要對@Bean注解的源碼進行簡單的剖析。
@Bean注解的源碼詳見:org.springframework.context.annotation.Bean,如下所示。
/**
* @author Rod Johnson
* @author Costin Leau
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
//Since: 4.3.3
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
//Since: 5.1
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
從@Bean源碼的注釋可以看出,@Bean注解是Spring從3.0版本開始提供的注解,注解中各個屬性的含義如下所示。
name:String[]數(shù)組類型,指定注入到IOC容器中的Bean的名稱,可以指定多個名稱。如果不指定name屬性和value屬性的值,則注入到IOC容器中的Bean的名稱默認是方法的名稱。
value:String[]數(shù)組類型,從Spring 4.3.3版本開始提供的@Bean的屬性,作用與name屬性相同。
autowireCandidate:boolean類型,表示是否支持自動按照類型注入到其他的Bean中。此屬性會影響@Autowired注解,不會響應(yīng)@Resource注解,默認為true,表示支持自動按照類型注入到其他的Bean中。
initMethod:指定初始化的方法。
destroyMethod:指定銷毀的方法。
2.2 注解使用場景
在使用Spring的注解開發(fā)應(yīng)用程序時,如果是我們自己開發(fā)的類,可以在類上標(biāo)注@Component注解(也可以是@Repository、@Service、@Controller等注解),將類注入到IOC容器中。但是,有時很多類不是我們自己寫的,而是依賴的第三方的類庫,此時就無法在類上標(biāo)注@Component等注解了,此時就需要使用@Bean注解將其注入到IOC容器中。
也就是說,@Bean注解適用于將第三方提供的類注入到IOC容器中的場景。使用@Bean注解創(chuàng)建的Bean對象默認是單實例Bean對象。
三、使用案例
整個案例來玩玩兒吧!
3.1 案例描述
使用@Bean注解將User類的對象注入到IOC容器中,打印初始化和銷毀方法,并驗證使用@Bean注解創(chuàng)建的Bean對象默認是單實例Bean。
3.2 案例實現(xiàn)
使用@Bean注解可以將類對象注入到IOC容器中,并且@Bean注解的initMethod屬性可以指定初始化的方法,destroyMethod屬性可以指定銷毀的方法。當(dāng)IOC容器對Bean對象進行初始化時,會執(zhí)行@Bean注解的initMethod屬性指定的方法,當(dāng)IOC容器在關(guān)閉時,會執(zhí)行@Bean注解的destroyMethod屬性指定的方法,觸發(fā)銷毀方法的邏輯。
注意:上述邏輯僅限于注入到IOC容器中的單例Bean對象。如果是單實例Bean,則IOC容器啟動時,就會創(chuàng)建Bean對象,IOC容器關(guān)閉時,銷毀Bean對象。如果是多實例Bean,IOC容器在啟動時,不會創(chuàng)建Bean對象,在每次從IOC容器中獲取Bean對象時,都會創(chuàng)建新的Bean對象返回,IOC容器關(guān)閉時,也不會銷毀對象。也就是說,如果是多實例Bean,IOC容器不會管理Bean對象。
本節(jié),就以單實例Bean為例來實現(xiàn)案例程序,具體的實現(xiàn)步驟如下所示。
(1)新建注入到IOC容器中的User類
源碼詳見:spring-annotation-chapter-03工程下的io.binghe.spring.annotation.chapter03.bean.User,如下所示。
public class User {
private final Logger logger = LoggerFactory.getLogger(User.class);
public User(){
logger.info("執(zhí)行構(gòu)造方法...");
}
public void init(){
logger.info("執(zhí)行初始化方法...");
}
public void destroy(){
logger.info("執(zhí)行銷毀方法...");
}
}IOC容器啟動時,會創(chuàng)建User類的對象并調(diào)用init()方法進行初始化。IOC容器關(guān)閉時,會銷毀User類的對象,并調(diào)用destroy()方法執(zhí)行銷毀邏輯。
可以看到,User類的實現(xiàn)比較簡單,提供了一個無參構(gòu)造方法,一個表示初始化的init()方法和一個表示銷毀的destroy()方法,每個方法中都打印了相應(yīng)的日志來說明調(diào)用了對應(yīng)的方法。
(2)創(chuàng)建Spring的配置類BeanConfig
源碼詳見:spring-annotation-chapter-03工程下的io.binghe.spring.annotation.chapter03.config.BeanConfig,如下所示。
@Configuration
@ComponentScan(basePackages = "io.binghe.spring.annotation.chapter03")
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public User user(){
return new User();
}
}
可以看到,在BeanConfig類上標(biāo)注了@Configuration注解,說明BeanConfig類是一個Spring的配置類,并且在BeanConfig類上標(biāo)注了@ComponentScan注解,指定要掃描的包為io.binghe.spring.annotation.chapter03。
在BeanConfig類中定義了一個user()方法,返回一個User對象。在user()方法上標(biāo)注了@Bean注解,并通過initMethod屬性指定的初始化方法為User類的init()方法,通過destroyMethod屬性指定的銷毀方法為User類的destroy()方法。
(3)創(chuàng)建案例測試類BeanTest
源碼詳見:spring-annotation-chapter-03工程下的io.binghe.spring.annotation.chapter03.BeanTest,如下所示。
public class BeanTest {
private static final Logger LOGGER = LoggerFactory.getLogger(BeanTest.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
LOGGER.info("IOC容器啟動完成....");
User user1 = context.getBean(User.class);
User user2 = context.getBean(User.class);
LOGGER.info("user1是否等于user2===>>>{}", (user1 == user2));
context.close();
}
}可以看到,在BeanTest類中,通過BeanConfig配置類創(chuàng)建了IOC容器,從IOC容器中兩次獲取User對象,分別賦值給user1和user2,打印user1是否等于user2的日志,并關(guān)閉IOC容器。
3.3 案例測試
運行BeanTest類的main()方法,輸出的日志信息如下所示。
- 執(zhí)行構(gòu)造方法...
- 執(zhí)行初始化方法...
- IOC容器啟動完成....
- user1是否等于user2===>>>true
- 執(zhí)行銷毀方法...
可以看到,IOC容器在啟動時,就創(chuàng)建了User對象,并調(diào)用了@Bean注解的initMethod方法指定的初始化方法,IOC容器在關(guān)閉時,調(diào)用@Bean注解的destroyMethod屬性指定的銷毀方法。并且通過打印的user1是否等于user2的日志為true,可以說明使用@Bean注解默認創(chuàng)建的Bean對象是單實例Bean,每個類在IOC容器中只會存在一個單實例Bean對象。
四、源碼時序圖
結(jié)合時序圖理解源碼會事半功倍,你覺得呢?
本節(jié),就以源碼時序圖的方式,直觀的感受下@Bean注解在Spring源碼層面的執(zhí)行流程。@Bean注解在Spring源碼層面的執(zhí)行流程如圖3-1和圖3-2所示。
圖3-1
圖3-2
由圖3-1和圖3-2可以看出,@Bean注解在Spring源碼層面的執(zhí)行流程會涉及到BeanTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、ConfigurationClassBeanDefinitionReader類和DefaultListableBeanFactory類。具體的源碼執(zhí)行細節(jié)參見源碼解析部分。
五、源碼解析
源碼時序圖整清楚了,那就整源碼解析唄!
@Bean注解在Spring源碼層面的執(zhí)行流程,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻。
(1)運行案例程序啟動類
案例程序啟動類源碼詳見:spring-annotation-chapter-03工程下的io.binghe.spring.annotation.chapter03.BeanTest,運行BeanTest類的main()方法。
在BeanTest類的main()方法中調(diào)用了AnnotationConfigApplicationContext類的構(gòu)造方法,并傳入了ComponentScanConfig類的Class對象來創(chuàng)建IOC容器。接下來,會進入AnnotationConfigApplicationContext類的構(gòu)造方法。
(2)解析AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class>... componentClasses)構(gòu)造方法
源碼詳見:org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class>... componentClasses)。
public AnnotationConfigApplicationContext(Class>... componentClasses) {
this();
register(componentClasses);
refresh();
}可以看到,在上述構(gòu)造方法中,調(diào)用了refresh()方法來刷新IOC容器。
(3)解析AbstractApplicationContext類的refresh()方法
源碼詳見:org.springframework.context.support.AbstractApplicationContext#refresh()。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//############省略其他代碼##############
try {
//############省略其他代碼##############
invokeBeanFactoryPostProcessors(beanFactory);
//############省略其他代碼##############
}catch (BeansException ex) {
//############省略其他代碼##############
}finally {
//############省略其他代碼##############
}
}
}
refresh()方法是Spring中一個非常重要的方法,很多重要的功能和特性都是通過refresh()方法進行注入的??梢钥吹?,在refresh()方法中,調(diào)用了invokeBeanFactoryPostProcessors()方法。
(4)解析AbstractApplicationContext類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
源碼詳見:org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}可以看到,在AbstractApplicationContext類的invokeBeanFactoryPostProcessors()方法中調(diào)用了PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors()方法。
(5)解析PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法
源碼詳見:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)。
由于方法的源碼比較長,這里,只關(guān)注當(dāng)前最核心的邏輯,如下所示。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors) {
//############省略其他代碼##############
ListcurrentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
//############省略其他代碼##############
}
可以看到,在PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法中,BeanDefinitionRegistryPostProcessor的實現(xiàn)類在執(zhí)行邏輯上會有先后順序,并且最終都會調(diào)用invokeBeanDefinitionRegistryPostProcessors()方法。
(6)解析PostProcessorRegistrationDelegate類的invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)方法
源碼詳見:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)。
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
可以看到,在invokeBeanDefinitionRegistryPostProcessors()方法中,會循環(huán)遍歷postProcessors集合中的每個元素,調(diào)用postProcessBeanDefinitionRegistry()方法注冊Bean的定義信息。
(7)解析ConfigurationClassPostProcessor類的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//##########省略其他代碼###################
processConfigBeanDefinitions(registry);
}
可以看到,在postProcessBeanDefinitionRegistry()方法中,會調(diào)用processConfigBeanDefinitions()方法。
(8)解析ConfigurationClassPostProcessor類的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry registry)。
這里,重點關(guān)注方法中的如下邏輯。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//############省略其他代碼#################
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
//############省略其他代碼#################
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
//############省略其他代碼#################
}
while (!candidates.isEmpty());
//############省略其他代碼#################
} 可以看到,在processConfigBeanDefinitions()方法中,創(chuàng)建了一個ConfigurationClassParser類型的對象parser,并且調(diào)用了parser的parse()方法來解析類的配置信息。
(9)解析ConfigurationClassParser類的parse(SetconfigCandidates)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#parse(SetconfigCandidates)。
public void parse(SetconfigCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
可以看到,在ConfigurationClassParser類的parse(SetconfigCandidates)方法中,調(diào)用了類中的另一個parse()方法。
(10)解析ConfigurationClassParser類的parse(AnnotationMetadata metadata, String beanName)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#parse(AnnotationMetadata metadata, String beanName)
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}可以看到,上述parse()方法的實現(xiàn)比較簡單,直接調(diào)用了processConfigurationClass()方法。
(11)解析ConfigurationClassParser類的processConfigurationClass(ConfigurationClass configClass, Predicatefilter)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass, Predicatefilter)。
protected void processConfigurationClass(ConfigurationClass configClass, Predicatefilter) throws IOException {
//###############省略其他代碼####################
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
可以看到,在processConfigurationClass()方法中,會通過do-while()循環(huán)獲取配置類和其父類的注解信息,SourceClass類中會封裝配置類上注解的詳細信息。在在processConfigurationClass()方法中,調(diào)用了doProcessConfigurationClass()方法。
(12)解析ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)。
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)
throws IOException {
//##################省略其他代碼##################
// Process individual @Bean methods
SetbeanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
//##################省略其他代碼##################
// No superclass -> processing is complete
return null;
}
這里,只關(guān)注與@Bean注解相關(guān)的方法,可以看到,在doProcessConfigurationClass()方法中調(diào)用了retrieveBeanMethodMetadata()方法來解析sourceClass中有關(guān)@Bean注解的屬性信息。
(13)解析ConfigurationClassParser類的retrieveBeanMethodMetadata(SourceClass sourceClass)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata(SourceClass sourceClass)。
private SetretrieveBeanMethodMetadata(SourceClass sourceClass) {
AnnotationMetadata original = sourceClass.getMetadata();
SetbeanMethods = original.getAnnotatedMethods(Bean.class.getName());
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
try {
AnnotationMetadata asm = this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
SetasmMethods = asm.getAnnotatedMethods(Bean.class.getName());
if (asmMethods.size() >= beanMethods.size()) {
SetselectedMethods = new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
}
}
return beanMethods;
}
可以看到,在retrieveBeanMethodMetadata()方法中主要是解析@Bean注解,并且將解析到的方法元數(shù)據(jù)存入 Set集合中并返回。
(14)回到ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。
這里,還是重點看下方法中與@Bean注解有關(guān)的代碼片段,如下所示。
SetbeanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
調(diào)用retrieveBeanMethodMetadata()方法獲取到標(biāo)注了@Bean注解的方法的元數(shù)據(jù)集合后,遍歷方法的元數(shù)據(jù)集合,將方法的元數(shù)據(jù)methodMetadata和配置類configClass傳入BeanMethod類的構(gòu)造方法,創(chuàng)建BeanMethod對象,并調(diào)用configClass的addBeanMethod()方法傳入創(chuàng)建的BeanMethod對象。
configClass的addBeanMethod()方法的源碼詳見:org.springframework.context.annotation.ConfigurationClass#addBeanMethod(BeanMethod method),如下所示。
void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}可以看到,在addBeanMethod()方法中,調(diào)用了beanMethods的add()方法添加BeanMethod對象。
beanMethods的源碼詳見:org.springframework.context.annotation.ConfigurationClass#beanMethods,如下所示。
private final SetbeanMethods = new LinkedHashSet<>();
可以看到,beanMethods是一個LinkedHashSet類型的集合。也就是說,在ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法中,會將解析出的標(biāo)注了@Bean注解的元數(shù)據(jù)封裝成BeanMethod對象,添加到一個LinkedHashSet類型的beanMethods集合中。
(15)回到ConfigurationClassPostProcessor類的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry registry)。
此時,重點關(guān)注下如下源碼片段。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//############省略其他代碼#################
do {
//############省略其他代碼#################
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
//############省略其他代碼#################
}
while (!candidates.isEmpty());
//############省略其他代碼#################
}可以看到,在processConfigBeanDefinitions()方法的do-while()循環(huán)中,調(diào)用了reader的loadBeanDefinitions()方法來加載Bean的定義信息。
(16)解析ConfigurationClassBeanDefinitionReader類的loadBeanDefinitions(SetconfigurationModel)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(SetconfigurationModel)
public void loadBeanDefinitions(SetconfigurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
可以看到,在loadBeanDefinitions()方法中,會循環(huán)遍歷,傳入的configurationModel集合,并調(diào)用loadBeanDefinitionsForConfigurationClass()方法處理遍歷的每個元素。
(17)解析ConfigurationClassBeanDefinitionReader類的loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator)。
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//###############省略其他代碼################
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//###############省略其他代碼################
}
可以看到,在loadBeanDefinitionsForConfigurationClass()方法中,會循環(huán)遍歷通過configClass獲取到的BeanMethod集合,并調(diào)用loadBeanDefinitionsForBeanMethod()方法處理遍歷的每個BeanMethod對象。
(18)解析ConfigurationClassBeanDefinitionReader類的loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod)
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
List names = new ArrayList<>(Arrays.asList(bean.getStrin
文章名稱:我們深度解析@Bean注解,你學(xué)會了嗎?
瀏覽路徑:http://m.fisionsoft.com.cn/article/cddiodo.html


咨詢
建站咨詢
