我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringIOC BeanDefinition的加载流程详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringIOC BeanDefinition的加载流程详解

一.前言

这一篇来看看 SpringIOC 里面的一个细节点 , 来简单看看 BeanDefinition 这个对象 , 以及有没有办法对其进行定制.

CASE 备份 : ? gitee.com/antblack/ca…

二. BeanDefinition 的体系

2.1 体系概览

这里面需要关注的几个类分别为 :

  • BeanDefinition 接口 : 顶层接口 , 抽象了Bean加载的方法
  • AbstractBeanDefinition : 提供了多数方法的默认实现
  • RootBeanDefinition : Spring BeanFactory 运行时统一的 BeanDefinition 视图
  • GenericBeanDefinition : 编程方式注册 BeanDefinition 的首选类
  • ChildBeanDefinition : 可继承BeanDefinition

下面来解释一下这里面说的一些概念 :

什么叫统一视图 ?

稍微从跟踪一下源码就能发现 , 从 xml 或者 JavaConfig 以及 Spring 默认加载的Bean配置类 ,最终都会被修饰为 RootBeanDefinition

GenericBeanDefinition 怎么用 ?

GenericBeanDefinition 是通过编程方式注入的 BeanDefinition 所对应的类 ,通常都是该类的子类 , 包括非Spring 的 ConfigBean 和 ServiceBean

ChildBeanDefinition 又做了什么 ?

一种可以继承 parent 配置的 BeanDefinition , 在加载环节中会通过 AbstractBeanFactory#getMergedLocalBeanDefinition() 来将 child 和 parent bean definition 进行合并。

  • BeanDefinition 进行 merge 操作时,会将 child 的属性与 parent 的属性进行合并,当有相同属性时,以 child 的为准
  • 如果是 Map 形式的配置 , 会取并集

2.2 BeanDefinition 的作用

  • 存储属性 : 基于接口 AttributeAccessor 实现
  • 存储元数据配置 : 基于 BeanMetadataElement 实现
  • 描述类的信息 : 包括Bean名称 , Primary 属性 , priority 配置 等等
  • Bean 的加载 : 例如 getBeansOfType , getBean 等等

总结其实就是一句话 : BeanDefinition 主要承载了Bean的元数据信息 ,同时描述了Bean在Spring体系中的加载方式 , 容器通过 BeanDefinition 中的配置来加载一个Bean

三. BeanDefinition 的载入

3.1 载入的入口

S1 : 启动配置类的载入

Spring 中第一个载入的 BeanDefinition 即为 RootBeanDefinition , 主要通过 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法进行加载

在这个环节中会通过加载的方式分别载入多个不同的 RootBeanDefinition , 这里是 Contain 关系 :

// internalConfigurationAnnotationProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   // 构建对应的 PostProcessor 并且载入
   RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// internalAutowiredAnnotationProcessor
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    //.....
}

在这个环节中 , 基本上都是在通过 registerPostProcessor 来注册各类加载类 , 我把这些看成根类 .

这些类通常为 Spring 进行服务 , 用来配置各类信息和加载封装 Bean

S2 : 普通配置类的载入

普通类的载入包括 SpringApplication 和一些自定义的个人配置类 , 这些类主要为了对非 Spring 的组件进行注册 , 配置 , 注入等操作

这一类通常通过 registerBean 来实现Bean的注册 , 注册的入口也很多 :

包括 AnnotatedBeanDefinitionReaderConfigurationClassPostProcessor等, 不难发现这一类 BeanDefinition 通常都是由 RootBeanDefinition 装载的类进行载入的

通常注册出来的对象也为 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子类等

3.2 保存的逻辑

BeanDefinition 会在 DefaultListableBeanFactory # registerBeanDefinition 中进行注册.

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
   // S1 : 会对 BeanDefinition 进行校验 , 主要是MethodOverrides和FactoryMethodName不能同时存在
   // -- 工厂方法必须创建具体的 Bean 实例 , 而 methodOverrides 会创建代理类且进行增强
   // -- 也就是说 工厂需要实例 , 不能是代理类
   if (beanDefinition instanceof AbstractBeanDefinition) {
       ((AbstractBeanDefinition) beanDefinition).validate();
   }
   // S2 : 判断 BeanDefinition 是否已经存在
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      // 是否允许同名Bean重写 , 因为此处已经存在一个了 , 不能重写则直接异常 
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }else if (existingDefinition.getRole() < beanDefinition.getRole()) {
          // 角色比较 ,只打日志 
          // ROLE_APPLICATION / ROLE_SUPPORT / ROLE_INFRASTRUCTURE
      }else if (!beanDefinition.equals(existingDefinition)) {
          // 判断是否为同一对象
      }
      // 以上主要是打log , 这里如果允许覆盖则直接覆盖了
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      // 判断该Bean是否已经开始初始化
      if (hasBeanCreationStarted()) {
         // 如果已经开始 , 需要对 Map 上锁后再处理
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 省略一些更新操作
         }
      }
      else {
         // 没有加载时的载入
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }
   // 如果Bean已经存在或已经开始加载了 , 这个时候时需要进行销毁操作的
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}
protected void resetBeanDefinition(String beanName) {
   // S1 : 如果已经创建 ,需要清空 Merge BeanDefinition
   clearMergedBeanDefinition(beanName);
   // S2 : 销毁 Bean
   destroySingleton(beanName);
   // S3 : 调用 PostProcessors 重置处理器进行处理
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      if (processor instanceof MergedBeanDefinitionPostProcessor) {
         ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
      }
   }
   // S4 : 如果该 BeanDefinition 是某个BeanDefinition 的Parent , 则需要同步处理
   for (String bdName : this.beanDefinitionNames) {
      if (!beanName.equals(bdName)) {
         BeanDefinition bd = this.beanDefinitionMap.get(bdName);
         if (bd != null && beanName.equals(bd.getParentName())) {
            resetBeanDefinition(bdName);
         }
      }
   }
}

3.3 使用的方式

BeanDefinition 的批量处理流程也是在 DefaultListableBeanFactory 中进行的

public void preInstantiateSingletons() throws BeansException {
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
   // 对所有的 BeanDefinition 进行循环处理
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      // 排除掉懒加载的Bean
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         // 此处省略工厂类的判断及处理 ...
         // 进入Bean获取逻辑
         getBean(beanName);
      }
   }
    //.....
}

总结

分析 BeanDefinition 不是这阶段的主要目的 , 后续会有几篇应用的文章来着重思考下如何进行业务定制

其实写源码文章是最轻松的 , 看懂就完事了 , 而写定制或者业务 , 往往写着写着发现有地方没搞懂 , 就需要回头继续看这个点 , 难度要大得多.......

附录 : BeanDefinition 功能一览

以上就是SpringIOC BeanDefinition的加载流程详解的详细内容,更多关于SpringIOC BeanDefinition加载的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringIOC BeanDefinition的加载流程详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SpringIOC BeanDefinition的加载流程详解

这篇文章主要为大家介绍了SpringIOC BeanDefinition的加载流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

SpringBootbean查询加载顺序流程详解

当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间件需要完成自动装配时。你会声明自己的Configuration类,但是可能你面对的是好几个有互相依赖的Bean
2023-03-12

SpringBoot配置的加载流程详细分析

了解内部原理是为了帮助我们做扩展,同时也是验证了一个人的学习能力,如果你想让自己的职业道路更上一层楼,这些底层的东西你是必须要会的,这篇文章主要介绍了SpringBoot配置的加载流程
2023-01-06

Flutter加载图片流程MultiFrameImageStreamCompleter解析

这篇文章主要为大家介绍了Flutter加载图片流程MultiFrameImageStreamCompleter示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-16

springmvc加载的流程是什么

Spring MVC的加载流程如下:客户端发送请求,请求被DispatcherServlet捕获。DispatcherServlet根据请求的URL找到对应的HandlerMapping,确定请求对应的Handler。HandlerAdap
2023-10-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录