本文探讨spring对bean解析,并注册到IOC容器的过程

一、ClassPathBeanDefinitionScanner类(遍历bean集合)

//类路径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);//遍历扫描到的Beanfor (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中。
接下来的步骤可以分为以下几个方面:

 1.遍历bean集合2.获取@Scope注解的值,即获取Bean的作用域3.为Bean生成名称4.给Bean的一些属性设置默认值5.检查Bean是否已在IOC容器中注册6.根据Bean的作用域,生成相应的代理模式7.把Bean放入IOC容器中

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

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

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {//默认是singletonScopeMetadata 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或者NOif (proxyMode == ScopedProxyMode.DEFAULT) {//设置proxyMode为NOproxyMode = this.defaultProxyMode;}//为返回的元数据设置proxyModemetadata.setScopedProxyMode(proxyMode);}}//返回解析的作用域元信息对象return metadata;}

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

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

value 属性就是我们常用的用来设置Bean的单例/多例
scopeName 作用域名称,如singleton、request
proxyMode 是用来设置代理方式的

这里的AnnotationAttributes是注解属性key-value的封装类,继承了LinkedHashMap,所以也是key-value形式的数据结构。

三、为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之前首先实例化所依赖的BeanAnnotationAttributes 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//获取容器中已存在的beanBeanDefinition 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,则返回falseboolean 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@Overridepublic 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");//校验解析的BeanDefinitonif (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 的 BeanDefinitionoldBeanDefinition = 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_INFRASTRUCTUREif (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 移除 beanNameif (this.manualSingletonNames.contains(beanName)) {Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);updatedSingletons.remove(beanName);this.manualSingletonNames = updatedSingletons;}}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.manualSingletonNames.remove(beanName);}this.frozenBeanDefinitionNames = null;}//检查是否有同名的BeanDefinition已经在IOC容器中注册
88      if (oldBeanDefinition != null || containsSingleton(beanName)) {//更新beanDefinitionNames 和 manualSingletonNamesresetBeanDefinition(beanName);}}

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

1.beanName和beanDefinition的合法性校验
2.根据beanName从IOC容器中判断是否已经注册过
3.根据isAllowBeanDefinitionOverriding变量来判断是否覆盖
4.如果存在根据覆盖规则,执行覆盖或者抛出异常
5.如果不存在,则put到IOC容器beanDefinitionMap中

private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

到这里,注册bean到IOC容器的过程就基本结束了,实际上IOC注册不是什么神秘的东西,说白了就是把beanName和bean存入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容器注册BeanDefinitionregistry.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

Bean的解析与注册相关推荐

  1. 【栖梧-源码-spring】@Bean从解析到注册到beanDefinitionMap

    [栖梧-源码-spring]@Bean从解析到注册到beanDefinitionMap 序幕 源码阅读技巧 本文说明 类 ConfigurationClassParser#doProcessConfi ...

  2. Spring IOC原理 Bean标签解析和Definition封装

    以下源码版本是 Spring 5.2.x IOC Inversion of Control 控制反转,关键实现是DI Dependency Injection,就必然涉及到有一个容器保存系统中所有托管 ...

  3. spring beans源码解读之--bean definiton解析器

    spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean ...

  4. 手写简版spring --5--资源加载器解析文件注册对象

    一.目标 在完成 Spring 的框架雏形后,现在我们可以通过单元测试进行手动操作Bean对象的定义.注册和属性填充,以及最终获取对象调用方法.但这里会有一个问题,就是如果实际使用这个 Spring ...

  5. 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程

    一.依赖注入DI 学习目标 1)搞清楚构造参数依赖注入的过程及类 2)搞清楚注解方式的属性依赖注入在哪里完成的. 学习思路 1)思考我们手写时是如何做的 2)读 spring 源码对比看它的实现 3) ...

  6. spring中默认标签Bean标签解析一

    在Spring种有两种标签,一种是默认标签,另一种是自定义标签.对两种标签的用法和解析方式存在着很大的不同. 首先分析的是默认标签的解析过程. 解析标签的入口代码 protected void par ...

  7. 2、组件注册-@Configuration@Bean给容器中注册组件

    2.组件注册-@Configuration&@Bean给容器中注册组件 2.1 创建maven项目 spring-annotation pom.xml文件添加 spring-context 依 ...

  8. 手写Spring-第二章-实现 Bean 的定义、注册、获取

    前言 上一章我们实现了一个简化的Bean容器,那么这一章开始,我们就要上一些强度了.这一章主要的目的是,让Spring容器来自动化实现Bean的创建,并且实现单例Bean的复用.在上一章,我们只实现了 ...

  9. 手写简版spring --2--实现Bean的定义、注册、获取

    一.目标 在上一章节我们初步依照 Spring Bean 容器的概念,实现了一个粗糙版本的代码实现.那么本章节我们需要结合已实现的 Spring Bean 容器进行功能完善,实现 Bean 容器关于 ...

最新文章

  1. python 函数返回值的特殊情况
  2. 以太坊和EOS的DApps数量飙升但用户量滞后
  3. 1073 Scientific Notation (20 分)【难度: 一般 / 知识点: 字符串 模拟】
  4. iOS - 使用 SQLite 数据库实现数据持久化
  5. founder of girton college
  6. buck电路matlab,buck变换器介绍_buck变换器matlab仿真
  7. 处理JS异常的一个想法
  8. 编写jmeter测试用例_Jmeter性能测试系列篇(十)--批量用例执行结果检查设置
  9. Syntax error, parameterized types are only available if source level is 1.5 or greater
  10. .NET Core 取消令牌:CancellationToken
  11. html5 input select,【Web前端问题】select如何实现既可以像input那样支持输入,又可以从下拉中选择? antd...
  12. [置顶] EasyUI提交表单
  13. CCS6的graph变灰解决办法
  14. 谷歌五笔输入法电脑版_不背字根,如何三天学会五笔输入法
  15. 代理记账和专职会计哪个更适合企业?
  16. SASS的安装及简单操作
  17. 10种优化Mac以获得最佳性能的简便方法
  18. java sub_java调用zeromq PUB-SUB模式
  19. 趣味小游戏——扫雷(优化版)
  20. vmbox设置ubuntu共享文件夹_为什么共享文件夹、打印机访问还是受限?这几个设置解决90%问题...

热门文章

  1. 安全 - 堡垒机 - Jumpserver
  2. PagedList.Mvc只有一行时不显示分页
  3. pycharm中报错:ImportError: No module named 'skimage'
  4. 44)PHP,多个数据库查询(我没试)
  5. XML学习笔记(二)-- DTD格式规范
  6. Uber新功能:隐藏司机乘客们的手机号码
  7. Swift - 类型属性(类静态属性)和类方法(类静态方法)
  8. ubuntu下软件删除
  9. 笔记本在Win7下使用wubi安装Ubuntu10.04 双系统
  10. UEFI主板GPT方式安装CentOS 6.4