Spring IOC源码分析一:注册beanDefinition

前言

本篇文章的主要目的是梳理Spring 容器启动时bean 加载的主流程和相关核心逻辑。

本篇文章主要分析以注解的方式加载bean,以容器初始化singleton bean为主流程,singleton bean在容器初始化时就被实例化。

测试用例

本文所有代码示例来源于Spring 5.1.x版本。

@Configuration
@Import(NameConfig.class)
//@Import(AspectJAutoProxyRegistrar.class)
//@ImportResource("org/springframework/aop/aspectj/AfterAdviceBindingTests.xml")
static class AutowiredConfig {

    @Autowired
    private String name;

    @Bean TestBean testBean() {
	    TestBean testBean = new TestBean();
	    testBean.name = name;
	    return testBean;
    }
}
@Configuration
static class NameConfig {

    @Bean String name() { return "foo"; }
}
public class AnnotationConfigApplicationContextTests {

	@Test
	public void scanAndRefresh() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.annotation6");
		context.refresh();

		context.getBean(uncapitalize(ConfigForScanning.class.getSimpleName()));
		context.getBean("testBean"); // contributed by ConfigForScanning
		context.getBean(uncapitalize(ComponentForScanning.class.getSimpleName()));
		context.getBean(uncapitalize(Jsr330NamedForScanning.class.getSimpleName()));
		Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
		assertEquals(1, beans.size());
	}

	@Test
	public void registerAndRefresh() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AutowiredConfig.class);
		//context.register(Config.class, NameConfig.class);
		context.refresh();

		context.getBean("testBean");
		context.getBean("name");
		Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
		assertEquals(2, beans.size());
	}
}

上述测试用例均来源与spring项目的test

容器初始化

先说明几个在Spring 初始化容器中非常重要的对象

  • BeanDefinition 对象:容器中的每一个 bean 都会有一个对应的 BeanDefinition 实例。该实例负责保存 bean 对象的所有必要信息,包括 bean 对象的 class 类型、是否是抽象类、构造方法和参数、其他属性等等。
  • BeanDefinitionRegistry 对象: 抽象出 bean 的注册逻辑。registerBeanDefinition、removeBeanDefinition、getBeanDefinition 等注册管理 BeanDefinition 的方法。
  • BeanFactory 对象: 则抽象出了 bean 的管理逻辑。主要包含 getBean、containBean、getType、getAliases 等管理 bean 的方法。
  • DefaultListableBeanFactory 对象: 作为一个比较通用的 BeanFactory 实现,它同时也实现了 BeanDefinitionRegistry 接口,因此它就承担了 bean 的注册管理工作,这是常用的注册管理bean 的beanFactory。
AnnotationConfigApplicationContext
public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	//直接将注解bean注册到容器,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean自动注册到容器中
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
}

从源码中可以看出,空参的构造方法会先初始化sacner和reader,为进行bean的解析注册做准备,而容器里不包含任何Bean信息,从注释

/**
     * Create a new AnnotationConfigApplicationContext that needs to be populated
     * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
 */

可以看出,如果调用空参构造,需要手动调用其register()方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程。

而BeanFactory是在哪里初始化的呢?
从AnnotationConfigApplicationContext父类中,可以找到beanFactory的初始化。
GenericApplicationContext#new

public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}

new DefaultListableBeanFactory()过程中会初始化所有父类的属性以及加载各种配置,如beanClassLoader,InstantiationStrategy等。

然后在初始化AnnotatedBeanDefinitionReader过程中,会初始化spring inner-post-processor,包括BeanPostProcessor和BeanFactoryPostProcessor(比如ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessor)。
AnnotatedBeanDefinitionReader#new

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

AnnotationConfigUtils#registerAnnotationConfigProcessors

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}

		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		...
		return beanDefs;
}

而在初始化ClassPathBeanDefinitionScanner时,会注册过滤器,也就是需要扫描什么注解,比如@Component。
ClassPathScanningCandidateComponentProvider#registerDefaultFilters

protected void registerDefaultFilters() {
	//向要包含的过滤规则中添加@Component注解类,注意Spring中@Repository
	//@Service和@Controller都是Component,因为这些注解都添加了@Component注解
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	//获取当前类的类加载器
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	//向要包含的过滤规则添加JavaEE6的@ManagedBean注解
	this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));

	//向要包含的过滤规则添加@Named注解
	this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
}

此时容器的初始化完成。开始注册bean Definition

解析并注册bean definition

registerBean

Spring 提供两种bean的注册方式,一种时通过扫描路径来加载,另一种则是通过class文件来注册。先来看下class文件注册。

@Test
public void registerAndRefresh() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	context.register(AutowiredConfig.class);
	context.refresh();

	context.getBean("testBean");
	context.getBean("name");
	Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
	assertEquals(2, beans.size());
}

主要看context.register(AutowiredConfig.class)方法,主要流程如下:
在这里插入图片描述
AnnotationBeanDefinitionReader#doRegisterBean
先看源码:

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
							@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		//根据指定的注解Bean定义类,创建Spring容器中对注解Bean的封装的数据结构
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
		//用来解析注释了@condition注解的类,不满足条件跳过
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}
		//设置创建bean实例的回调
		abd.setInstanceSupplier(instanceSupplier);
		//解析注解Bean定义的作用域
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		//为注解Bean定义设置作用域
		abd.setScope(scopeMetadata.getScopeName());
		//为注解Bean生成Bean名称
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		//处理注解Bean定义中的通用注解
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		//如果在向容器注册注解Bean定义时,使用了额外的限定符注解,则解析限定符注解。
		//主要是配置的关于autowiring自动依赖注入装配的限定条件,即@Qualifier注解,Spring自动依赖注入装配默认是按类型装配,如果使用@Qualifier则按名称
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				//如果配置了@Primary注解,设置该Bean为autowiring自动依赖注入装配时的首选
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				//如果配置了@Lazy注解,则设置该Bean为非延迟初始化,如果没有配置,则该Bean为预实例化
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				//如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一
				//个autowiring自动依赖注入装配限定符,该Bean在进autowiring
				//自动依赖注入装配时,根据名称装配限定符指定的Bean
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		/**
		 * @see AnnotationConfigApplicationContextTests#individualBeanWithSupplier()
		 */
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}
		//创建一个指定Bean名称的Bean定义对象,封装注解Bean定义类数据
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		//根据注解Bean定义类中配置的作用域,创建相应的代理对象
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		//向IoC容器注册注解Bean类定义对象
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

可以看出,就是将annotatedClass解析成BeanDefinition,并将bean的属性例如@Lazy等封装进去,接着调用beanFactory的注册:
DefaultListableBeanFactory#registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		//从缓存中取出当前bean的 beanDefinition
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//如果存在
		if (existingDefinition != null) {
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		//如果不存在
		else {
			//检查该bean是否已开始创建
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					//如果单例模式的bean名单中有该bean的name,那么移除掉它。
					//也就是说着,将一个原本是单例模式的bean重新注册成一个普通的bean
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
		
}

BeanDefinition的注册分为步:

  1. 从缓存中取出当前bean的BeanDefinition
  2. 如果存在,则用当前BeanDefinition覆盖原有的
  3. 如果不存在,判断当前bean是否以及开始创建
  4. 如果没有开始创建,则将当前BeanDefinition,以及beanName放入缓存
  5. 如果已经开始创建,将当前BeanDefinition和beanName放入缓存后,如果当前bean是manual singleton bean,则将当前beanName从manual singleton Bean Name中移出,也就是变成了普通的bean,这里的manual singleton Bean指的是以下几种bean:
    在这里插入图片描述
scanBean
	@Test
	public void scanAndRefresh() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.annotation6");
		context.refresh();

		context.getBean(uncapitalize(ConfigForScanning.class.getSimpleName()));
		context.getBean("testBean"); // contributed by ConfigForScanning
		context.getBean(uncapitalize(ComponentForScanning.class.getSimpleName()));
		context.getBean(uncapitalize(Jsr330NamedForScanning.class.getSimpleName()));
		Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
		assertEquals(1, beans.size());
	}

还是先看context.scan()方法。主要流程如下:
scan概览
ClassPathBeanDefinitionScanner#doScan

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			//扫描给定类路径,获取符合条件的Bean定义
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				//获取Bean定义类中@Scope注解的值,即获取Bean的作用域
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				//生成bean name
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
				//设置Bean的自动依赖注入装配属性等
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
				if (candidate instanceof AnnotatedBeanDefinition) {
					//处理注解Bean中通用的注解
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					//根据注解中配置的作用域,为Bean应用相应的代理模式
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

这个其实和registerBean差不多,只是多了一步从扫描路径里获取符合的bean并封装到beanDefinition中,然后就是registerBean的流程
ClassPathScanningCandidateComponentProvider#scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
			//解析给定的包路径,this.resourcePattern=” **/*.class”,
			//ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX=“classpath:”
			//resolveBasePackage方法将包名中的”.”转换为文件系统的”/”
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			//将给定的包路径解析为Spring资源对象
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			for (Resource resource : resources) {
				
				if (resource.isReadable()) {
						//为指定资源获取元数据读取器,元信息读取器通过汇编(ASM)读取资源元信息
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						//如果扫描到的类符合容器配置的过滤规则
						if (isCandidateComponent(metadataReader)) {
							//通过汇编(ASM)读取资源字节码中的Bean定义元信息
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							//设置Bean定义来源于resource
							sbd.setResource(resource);
							//为元数据元素设置配置资源对象
							sbd.setSource(resource);
							//检查Bean是否是一个可实例化的对象
							if (isCandidateComponent(sbd)) {
								
								candidates.add(sbd);
							}
				}
		}
		return candidates;
	}

以上。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值