首页 > 其他分享 >AnnotationConfigApplicationContext流程看@Configuration,@ComponentScan,其它注解bean的注册

AnnotationConfigApplicationContext流程看@Configuration,@ComponentScan,其它注解bean的注册

时间:2025-01-01 22:26:38浏览次数:3  
标签:AnnotationConfigApplicationContext beanFactory BeanFactoryPostProcessor Componen

目录

AnnotationConfigApplicationContext 测试代码

@Test 
	public void testAdd() throws Exception {
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(LocalConfig.class, LocalConfig2.class);

		UserService service = (UserService)ctx.getBean("userService");
        Assert.assertNotNull(service);
		service.add(new User());

		service.destroy();
	}

如下直接开始debug流程

目录可以设置如下
在这里插入图片描述

手动注册第1个bean: LocalConfig

在这里插入图片描述

@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.aop.test")
public class LocalConfig {

    public LocalConfig(){
        System.out.println("LocalConfig()");
    }

}

手动注册第2个bean: LocalConfig2

在这里插入图片描述

@EnableAspectJAutoProxy
@Configuration
public class LocalConfig2 {

    public LocalConfig2(){
        System.out.println("LocalConfig2()");
    }

    @Bean
    public X x() {
        System.out.println("start new X");
        return new X();
    }
}

refresh方法执行前

  • 两个手动注册的bean是AnnotatedGenericBeanDefinition类型
    在这里插入图片描述
  • beanFactoryPostProcessors 数量为0
    在这里插入图片描述

技巧

备注:可以在关心的地方执行表达式查看自己关心的东西是否变化了; 如下关心beanDefinitionMap是否变化
在这里插入图片描述
可以添加watch (debug技巧更多则要google下了)
在这里插入图片描述

refresh的postProcessBeanFactory方法

在本例中不对如下两个产生变化,先跳过

((AnnotationConfigApplicationContext) this).beanFactoryPostProcessors

((AnnotationConfigApplicationContext) this).beanFactory.beanDefinitionMap

可以看下这个方法的描述:

/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for registering special
	 * BeanPostProcessors etc in certain ApplicationContext implementations.
	 * @param beanFactory the bean factory used by the application context
	 */
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	}

可以自己加入BeanPostProcessors进行扩展

refresh的 invokeBeanFactoryPostProcessors(beanFactory);

这个步骤执行完成后发生了变化,需要深入细节了
在这里插入图片描述

BeanDefinitionRegistry执行所有的BeanFactoryPostProcessor,首先执行的是BeanDefinitionRegistryPostProcessor

本例中没有自定义BeanFactoryPostProcessor, 所有跳过。

补充:BeanDefinitionRegistry是什么?

BeanDefinitionRegistry是个接口,负责注册和管理Bean的定义信息。

/**
 * Interface for registries that hold bean definitions, for example RootBeanDefinition
 * and ChildBeanDefinition instances. Typically implemented by BeanFactories that
 * internally work with the AbstractBeanDefinition hierarchy.
 *
 * <p>This is the only interface in Spring's bean factory packages that encapsulates
 * <i>registration</i> of bean definitions. The standard BeanFactory interfaces
 * only cover access to a <i>fully configured factory instance</i>.
 *
 * <p>Spring's bean definition readers expect to work on an implementation of this
 * interface. Known implementors within the Spring core are DefaultListableBeanFactory
 * and GenericApplicationContext.
 *
 * @author Juergen Hoeller
 * @since 26.11.2003
 * @see org.springframework.beans.factory.config.BeanDefinition
 * @see AbstractBeanDefinition
 * @see RootBeanDefinition
 * @see ChildBeanDefinition
 * @see DefaultListableBeanFactory
 * @see org.springframework.context.support.GenericApplicationContext
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see PropertiesBeanDefinitionReader
 */
public interface BeanDefinitionRegistry extends AliasRegistry {

内部的BeanDefinitionRegistryPostProcessor执行,具体是ConfigurationClassPostProcessor

在这里插入图片描述

附:ConfigurationClassPostProcessor类图

在这里插入图片描述

(*)ConfigurationClassPostProcessor执行processConfigBeanDefinitions方法

此句执行完可以看到beanDefinitionMap发生了变化,所以重点查看
在这里插入图片描述
对两个@Configurationonfig bean进行解析

ConfigurationClassParser#parse(BeanDefinitionHolder)解析过程
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

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

	processDeferredImportSelectors();
}
ConfigurationClassParser#doProcessConfigurationClass

直接一看可以看到componentScan了
在这里插入图片描述

LocalConfig配置的@ComponentScan扫描出beanDefinition并添加到beanDefinitionMap中

在这里插入图片描述

可以看到如下
在这里插入图片描述

如@import @Resource @Bean等也可以看到在此解析出来

执行到LocalConfig2全配置类可以看到解析出@Bean方法
在这里插入图片描述

全注解类加上扫描出来的可能的bean,再次loadBeanDefinitions得到最后所有bean

在这里插入图片描述

执行完成后如下:
在这里插入图片描述

do whitle(!candidates.isEmpty())处理完

观察代码可以看到是嵌套处理的,因为扫描出来的可能继续成为全注解类,不断的处理,直到完成,有点递归的感觉

do {
	parser.parse(candidates);
	parser.validate();

	Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
	configClasses.removeAll(alreadyParsed);

	// Read the model and create bean definitions based on its content
	if (this.reader == null) {
		this.reader = new ConfigurationClassBeanDefinitionReader(
				registry, this.sourceExtractor, this.resourceLoader, this.environment,
				this.importBeanNameGenerator, parser.getImportRegistry());
	}
	this.reader.loadBeanDefinitions(configClasses);
	alreadyParsed.addAll(configClasses);

	candidates.clear();
	if (registry.getBeanDefinitionCount() > candidateNames.length) {
		String[] newCandidateNames = registry.getBeanDefinitionNames();
		Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
		Set<String> alreadyParsedClasses = new HashSet<String>();
		for (ConfigurationClass configurationClass : alreadyParsed) {
			alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
		}
		for (String candidateName : newCandidateNames) {
			if (!oldCandidateNames.contains(candidateName)) {
				BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
						!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
					candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
				}
			}
		}
		candidateNames = newCandidateNames;
	}
}
while (!candidates.isEmpty());
ImportRegistry

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 如下

// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
	if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
		singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
	}
}

了解功能,暂时跳过:
ImportAware 接口,提供被导入类的访问功能。当一个类实现了 ImportAware 接口,并且被通过 @Import 注解导入到其他配置类中,该类可以获得对导入它的 AnnotationMetadata 的访问权。

BeanDefinitionRegistry执行所有的BeanFactoryPostProcessor,接下来执行的是实现了Ordered的

经历上面漫长的执行链路后,回到主线,继续执行那些实现了Ordered的BeanFactoryPostProcessor

在这里插入图片描述

可以看到代码都是同上的,本例中没有实现,直接跳过

BeanDefinitionRegistry执行所有的BeanFactoryPostProcessor,继续执行其它常规的BeanFactoryPostProcessor

在这里插入图片描述

本例没有,跳过

执行完BeanDefinitionRegistry类型的BeanFactoryPostProcessor后,类似的处理其它类型的BeanFactoryPostProcessor

具体如下:

// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : orderedPostProcessorNames) {
	orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(beanFactory, orderedPostProcessors);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
	nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();

所以可以看到BeanFactoryPostProcessor内部执行有区分,有先后次序

完成BeanDefinition注册,回到refresh方法继续

所以执行完invokeBeanFactoryPostProcessors(beanFactory); 后所有的BeanDefinition已经注册好了


总结

new AnnotationConfigApplicationContext(LocalConfig.class, LocalConfig2.class);
	register(annotatedClasses);
	refresh()
		invokeBeanFactoryPostProcessors(beanFactory);
			先BeanDefinitionRegistry类型的;按照内部的、order顺序、其它依此执行
				其中重要的BeanDefinitionRegistry类型的BeanFactoryPostProcessor为ConfigurationClassPostProcessor
			再PriorityOrdered的BeanFactoryPostProcessors
			再Ordered的
			最后其它的
		至此完成invokeBeanFactoryPostProcessors方法的执行,完成beanDefinition的注册

而ConfigurationClassPostProcessor执行过程中,会对诸如@ComponentScan @Import, @Bean等解析; 所以了解细节可以再次认真读一下ConfigurationClassPostProcessor代码,其过程是复杂的。 整个流程有很多可以借鉴的代码写法

标签:AnnotationConfigApplicationContext,beanFactory,BeanFactoryPostProcessor,Componen
From: https://blog.csdn.net/qq_26437925/article/details/144865082

相关文章

  • Spring IOC容器初始化:一场关于Bean生命周期的探险
    想象一下,SpringIOC容器就像一个大型的仓库,它的任务是存储和管理各种物品(在这里,物品就是Bean)。但是,在仓库开始工作之前,它需要先进行一系列的准备工作,这就是IOC容器的初始化过程。这个过程可以分为三个主要步骤:1.Resource定位:  •这一步就像是仓库管理员先要去找到存放......
  • 如何理解springboot中的bean?
    让我从多个角度来解释SpringBoot中的Bean概念:###1.什么是BeanBean是由Spring容器管理的对象,简单来说就是:-一个Java对象(实例)-被Spring容器创建、管理和销毁-可以被其他组件使用(依赖注入)###2.Bean的定义方式1.**注解方式**:```java@Component//......
  • 超实用 JMeter BeanShell Sampler 教程
    超实用JMeterBeanShellSampler教程宝子们,今天咱继续深挖JMeter里超厉害的BeanShellSampler,这次多来点实际工作中的例子,让你彻底搞懂它!一、这是个啥玩意儿?BeanShellSampler就像是JMeter的一个秘密武器,能让你用Java代码给测试计划“开外挂”。当JMeter自带的功......
  • Java 编程中 Bean、PO、POJO、VO、Entity、Model、DTO 术语全面解析对比
    术语描述特点用途BeanJava语言中可重用的组件,通常具有属性和对应的getter、setter方法等遵循JavaBean规范,具有一定的封装性和可访问性用于构建Java应用程序的各种组件,如在框架中作为配置信息的载体等PO持久化对象,通常与数据库中的表结构相对应与数据库表结构紧......
  • spring专题笔记(六):bean的自动装配(自动化注入)-根据名字进行自动装配、根据类型进行自动
    目录一、根据名字进行自动装配--byName二、根据类型进行自动装配byType本文章主要是介绍spring的自动装配机制,用代码演示spring如何根据名字进行自动装配、如何根据类型进行自动装配。代码演示,通俗易懂。一、根据名字进行自动装配--byNameUserService类中调用了UserD......
  • 从源码角度查看SpringBoot是怎样获取到Bean的
    背景:我们都知道在SpringBoot启动类上添加@SpringBootApplication注解后执行main方法就可以自动启动服务Spring会自动帮我们找到需要管理的Bean的呢探究:经典的八股文AbstractApplicationContext#refresh()方法相信大家已经比较熟悉了进入invokeBeanFactoryPostProcess......
  • 通过Java Bean Validation API
    通过JavaBeanValidationAPI的验证,对字符串、集合、数组等对象的大小进行验证。(如:通过限制参数的大小,来防止SQL注入)1、添加依赖<!--HibernateValidator--><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactI......
  • Spring 中的 LocalSessionFactoryBean和LocalContainerEntityManagerFactoryBean
    Spring中的LocalSessionFactoryBean和LocalContainerEntityManagerFactoryBean|Id|Title|DateAdded|SourceUrl|PostType|Body|BlogId|Description|DateUpdated|IsMarkdown|EntryName|CreatedTime|IsActive|AutoDesc|AccessPermission||-------......
  • 解决 高版本SpringBoot整合Swagger 启动报错Failed to start bean ‘documentationPlu
    解决高版本SpringBoot整合Swagger启动报错Failedtostartbean‘documentationPluginsBootstrapper‘问题|Id|Title|DateAdded|SourceUrl|PostType|Body|BlogId|Description|DateUpdated|IsMarkdown|EntryName|CreatedTime|IsActive|AutoDesc|A......
  • Bean生命周期配置
    Bean生命周期配置init-method:指定类中的初始化方法名称destroy-method:指定类中销毁方法名称Bean实例化三种方式1)使用无参构造方法实例化​它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败<beanid="userDao"class="com.itheima.d......