您的位置:首页 > 编程语言 > Java开发

Spring IoC 源码分析 (基于注解) 之 Bean的解析与注册

2019-08-23 14:15 786 查看

在上一篇文章Spring IoC 源码分析 (基于注解) 之 包扫描中,我们介绍了Spring基于注解扫描包获取bean的过程。本文我们将一起探讨spring对bean解析,并注册到IOC容器的过程。

我们先接着看下面这段代码:

ClassPathBeanDefinitionScanner类

//类路径Bean定义扫描器扫描给定包及其子包
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
//创建一个集合,存放扫描到的BeanDefinition封装类
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//遍历扫描所有给定的包路径
for (String basePackage : basePackages) {
//调用父类ClassPathScanningCandidateComponentProvider的方法
//扫描给定类路径,获取符合条件的Bean定义
10			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//遍历扫描到的Bean
for (BeanDefinition candidate : candidates) {
//获取@Scope注解的值,即获取Bean的作用域
14				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
//为Bean设置作用域
candidate.setScope(scopeMetadata.getScopeName());
//为Bean生成名称
18				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中通用的注解,在分析注解Bean定义类读取器时已经分析过
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
30				if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//根据注解中配置的作用域,为Bean应用相应的代理模式
33					definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//向容器注册扫描到的Bean
37					registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

上篇文章我们主要分析了第10行的 findCandidateComponents(basePackage)方法, 该方法主要是从给定的包路径中扫描符合过滤规则的Bean,并存入集合beanDefinitions中。
接下来的步骤可以分为以下几个方面:

  • 遍历bean集合

  • 获取@Scope注解的值,即获取Bean的作用域

  • 为Bean生成名称

  • 给Bean的一些属性设置默认值

  • 检查Bean是否已在IOC容器中注册

  • 根据Bean的作用域,生成相应的代理模式

  • 把Bean放入IOC容器中

第二步、获取@Scope注解的值,即获取Bean的作用域

首先来看下 获取Bean作用域的过程,主要是上面第14行ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);这段代码,我们继续跟踪进去:
AnnotationScopeMetadataResolver类

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
//默认是singleton
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
//获取@Scope注解的值
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
//将获取到的@Scope注解的值设置到要返回的对象中
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
//获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
//如果@Scope的proxyMode属性为DEFAULT或者NO
if (proxyMode == ScopedProxyMode.DEFAULT) {
//设置proxyMode为NO
proxyMode = this.defaultProxyMode;
}
//为返回的元数据设置proxyMode
metadata.setScopedProxyMode(proxyMode);
}
}
//返回解析的作用域元信息对象
return metadata;
}

Bean的作用域是通过@Scope注解实现的,我们先来看下@Scope注解的属性:


可以看到@Scope注解有三个属性,

  • value属性就是我们常用的用来设置Bean的单例/多例

  • proxyMode是用来设置代理方式的

关于@Scope注解原理的详细分析,请看这篇文章<>,这里就不详细讲了。

这里的AnnotationAttributes是注解属性key-value的封装类,继承了LinkedHashMap,所以也是key-value形式的数据结构。 通过debug看一下,这个变量拿到的值:


可以看到获取到了3个属性的值,其中value = prototype就是该Bean的作用域,这里我设置的是多例,然后再把获取到的注解属性值赋值给ScopeMetadata

第三步、为Bean生成名称

再回到上面ClassPathBeanDefinitionScanner类的doScan()方法中的第18行, 把我们获取到的Bean的作用域赋值给Bean。
然后为Bean生成名字,代码String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
跟踪进去,在AnnotationBeanNameGenerator类中:

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}

这段代码很好理解,先从注解中获取Bean的名称,如果注解中没有设置,那么生成一个默认的Bean的名称,通过ClassUtils.getShortName(beanClassName)生成一个类名的小写开头驼峰的名字,如studentController

第四步、给Bean的一些属性设置默认值

主要是doScan()中的如下两个方法:

//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
//设置Bean的自动依赖注入装配属性等
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
if (candidate instanceof AnnotatedBeanDefinition) {
//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

首先看postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName)
跟踪进去会到如下方法:

public void applyDefaults(BeanDefinitionDefaults defaults) {
//懒加载
setLazyInit(defaults.isLazyInit());
//加载模式
setAutowireMode(defaults.getAutowireMode());
//依赖检查
setDependencyCheck(defaults.getDependencyCheck());
//bean初始化方法
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
//bean销毁方法
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);
}

这里应该很清晰了,给bean设置一些默认值,BeanDefinitionDefaults是一个Bean属性默认值的封装类,从该类获取各个属性的默认值,赋值给bean。
接着我们看AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)方法。
跟踪进去:

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
//如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}

else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
//如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入装配的首选对象
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
//如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称,
//容器将确保在实例化该Bean之前首先实例化所依赖的Bean
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}

if (abd instanceof AbstractBeanDefinition) {
AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
absBd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
absBd.setDescription(description.getString("value"));
}
}
}

这里主要是处理bean上一些常用的注解,如@Lazy、@Primary、@DependsOn
注释很清晰,这里就不赘言了。

第五步、检查Bean是否已在IOC容器中注册

跟踪doScan()中的第30行if (checkCandidate(beanName, candidate))方法:

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
//是否包含beanName了
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
//如果容器中已经存在同名bean
//获取容器中已存在的bean
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
//新bean旧bean进行比较
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}

可以看到,其实是通过调用IOC容器的containsBeanDefinition(beanName)方法,来判断该beanName是否已存在,而IOC容器实际上是一个map,这里底层其实就是通过调用map.containsKey(key)来实现的。

第六步、为Bean应用相应的代理模式

跟踪doScan()中的 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);方法

static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

//获取注解Bean定义类中@Scope注解的proxyMode属性值
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
//如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
//获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS
//则返回true,如果为INTERFACES,则返回false
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
//为注册的Bean创建相应模式的代理对象
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}

这里就用到第二步中我门获取到的@Scope注解的proxyMode属性,然后为bean设置代理模式。

第七步、注册Bean到IOC容器中

跟踪doScan()中的第37行registerBeanDefinition(definitionHolder, this.registry);方法

//将解析的BeanDefinitionHold注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
//获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition
第9行		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

直接看第9行的代码,继续跟踪进去:

//向IOC容器注册解析的BeanDefiniton
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

// 校验 beanName 与 beanDefinition 非空
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");

//校验解析的BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}

BeanDefinition oldBeanDefinition;

// 从容器中获取指定 beanName 的 BeanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 如果已经存在
if (oldBeanDefinition != null) {
// 如果存在但是不允许覆盖,抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
// 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中。
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//注册的过程中需要线程同步,以保证数据的一致性(因为有put、add、remove操作)
64				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;
// 从 manualSingletonNames 移除 beanName
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已经在IOC容器中注册
88		if (oldBeanDefinition != null || containsSingleton(beanName)) {
//更新beanDefinitionNames 和 manualSingletonNames
resetBeanDefinition(beanName);
}
}

这里就是向IOC容器中注册bean的核心代码,这段代码很长,我们分开来看,主要分为几个步骤:

  • beanNamebeanDefinition的合法性校验

  • 根据beanName从IOC容器中判断是否已经注册过
    根据isAllowBeanDefinitionOverriding变量来判断是否覆盖

  • 如果存在根据覆盖规则,执行覆盖或者抛出异常

  • 如果不存在,则put到IOC容器beanDefinitionMap
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

到这里,注册bean到IOC容器的过程就基本结束了,实际上IOC注册不是什么神秘的东西,说白了就是把beanNamebean存入map集合中

此时我们再返回看第七步的代码BeanDefinitionReaderUtils类的registerBeanDefinition()方法,可以看到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());我们已经分析完了,剩下的就是把bean的别名也注册进去就大功告成了。

//将解析的BeanDefinitionHold注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
//获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

总结:

IoC容器其实就是DefaultListableBeanFactory,它里面有一个map类型的beanDefinitionMap变量,来存储注册的bean

IoC容器初始化过程

  • 1、资源定位
    扫描包路径下.class文件,将资源转为Resource

  • 2、资源加载
    通过ASM框架获取class元数据,封装到BeanDefinition

  • 3、资源解析
    获取bean上注解的属性值。如@Scope

  • 4、生成Bean
    生成beanName,设置Bean默认值(懒加载、初始化方法等)、代理模式

  • 5、注册Bean
    BeanDefinition放入IoC容器DefaultListableBeanFactory

最后思考几个小疑问

  • beanDefinitionMapConcurrentHashMap类型的,应该是线程安全的,但是为什么在代码64行的地方,还要加sync锁呢?

  • 之前已经检查过容器中是否有重名bean了,为什么在88行还要再检查一次呢?

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring