• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

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

Spring winrains 来源:大王叫下 1年前 (2019-10-13) 32次浏览
在上一篇文章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
    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);
        }
    }
}

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

//向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行还要再检查一次呢?

作者:大王叫下

来源:https://juejin.im/post/5d5b5702f265da03ef7a1494


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)