@Bean放入其引用Bean中初始化失败分析

2023-05-16

以下讨论的问题及术语均在SpringBoot框架下,问题十分小众,仅做整理记录。


1. 先说重点

  1. Bean依赖属性的注入顺序,与代码定义顺序无关;
  2. 最好是将@Bean注解配置的Bean放在@Configuration注解修饰的专门用于配置的类中;

2. 问题背景

为了方便,将使用注解(@Bean)方法生成的Bean的方法体定义在了使用此Bean的类中, 代码结构如下(为了描述方便,后文我们姑且将initBeanTestService叫做外层Bean,needInitBean叫做内层Bean):
错误代码
编写单元测试,运行printInitBeanValue方法,并在方法体内打断点便于观察属性值,

单元测试:
单元测试
运行单元测试会发现,通过内层Bean的属性值needInitValue的值为null,而外层Bean的属性值needInitValue有值,说明在初始化needInitBean时,外层Bean的属性值initValue并未注入成功,

运行结果:
测试结果
简单理下思路,因为外层Bean的类通过@Service注解进行修饰,所以SpringBoot在启动时会扫描到此注解进行Bean的初始化,初始化时会发现此Bean依赖initValueneeInitBean两个属性,读配置拿到initValue的值,然后去容器中查找是否有needInitBean存在,显然并不存在,于是要先初始化needInitBean,即内层Bean;内层bean的初始化,依赖于外层bean的initValue属性值,从现象来看,此时initValue无值,我们有以下疑问:

此initValue为什么没有值?外层Bean按理说应该已经初始化一半了。


3. 调用栈追踪

为了解释上述问题1,我们在@Bean注解修饰的方法体内打断点,从内层Bean的初始化开始,沿着断点处的调用栈倒着追踪,

  1. 首先是一些反射包下的方法;

  2. 一些BeanFactory初始化bean的方法;

  3. 找到AbstractBeanFactory中,发现此处开始创建needInitBean,那么上边的调用方就是初始化此Bean的触发点;

  4. 找到CommonAnnotationBeanPostProcessor,发现是此处为触发点;

  5. CommonAnnotationBeanPostProcessor一番游历,发现此处的逻辑是向外层Bean中注入依赖,找到319行,findResourceMetadata,此方法为找到需要注入的属性或方法的元数据,紧接着321行,为依赖注入逻辑(当然,若依赖是Bean,则去BeanFactory请求,找不到则进行初始化);
    注入
    点进去findResourceMetadata方法看看他是咋找要注入的属性的,包了一层缓存,主要逻辑在buildResourceMetadata方法,这里我们会发现,他遍历了各个属性和方法,找到有特定注解的属性和方法,放到了待注入的列表。其中注解就包括了我们熟悉的,也是外层bean中needInitBean头上的@Resource。但是并没有发现我们同样熟悉的@Value@Autowire

    resource

  6. 继续跟着调用栈往下走,到AbstractAutowireCaptableBeanFactory中,发现有一个循环去遍历BeanPostProceccer, 并过滤出InstantiationAwareBeanPostProcessor,对创建中的Bean进行处理,展开BeanPostProceccer的列表,会发现我们上边看到的CommonAnnotationBeanPostProcessor后边还有个AutowiredAnnotationBeanPostProcessor,此类也继承自InstantiationAwareBeanPostProcessor, 所以也会遍历到,然后我们就会发现他与5中描述的逻辑类似,也是先找到需要注入的属性,然后执行注入。不同的是它解析@Value@Autowire注解的属性为需要注入的属性;
    在这里插入图片描述

  7. 6中提到的遍历逻辑,是在对外层Bean进行依赖注入,即外层Bean的初始化过程,因为外层Bean是@Service注解修饰的,所以会在SpringBoot启动时扫描到进行初始化,所以我们再往下走没几步就到了SpringApplication.run


4. 问题出现逻辑梳理

  1. 应用启动,扫描@Service注解修饰的外层Bean,对其进行初始化;

  2. Bean的初始化由若干实现InstantiationAwareBeanPostProcessor接口的类在一个循环中依次对Bean进行处理;

  3. 循环中负责依赖注入的类CommonAnnotationBeanPostProcessor发现属性needInitBean@Resource修饰,需要进行注入,此时BeanFactory中没有needInitBean这个Bean,故对其进行初始化,此时外层Bean的initValue还没有注入进来,所以内层Bean初始化needInitValuenull

  4. 循环中负责依赖注入的类AutowiredAnnotationBeanPostProcessor发现属性initValue@Value修饰,需要进行注入,执行注入;

  5. 完成外层Bean的创建;


5. 结论

通过上述追踪,我们可以得出出现我们最初问题的原因:由于@Value@Resource在注入时并非用一个类进行注入,存在先后关系,故虽然外层Bean已经初始化一半去初始化内层Bean,initValue仍然没有值。

另外退一步说,如果我们使用的是@Autowire,而不是@Resource@Autowire@Value是由同一个BeanPostProceccer进行注入的,是不是@Value写在前面,本程序就能通呢?运行了一下是可以的,然而这并不严谨,因为就算是同一个BeanPostProceccer进行注入, 其属性的注入顺序是依赖反射包下的Class.getDeclaredFields方法获得的,而此方法注释明确写道,返回的数组是无序的

所以我们尽量还是避免这种写法,将@Bean注解配置的Bean放在@Configuration注解修饰的专门用于配置的类中较为稳妥。

ps: 如果我们将initValue使用属性注入,而needInitBean使用@Autowire修饰setter注入,可以保证严谨,因为目前的实现都是先进行属性注入在进行方法注入,不提倡。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@Bean放入其引用Bean中初始化失败分析 的相关文章

  • 3.2.1 通过扫描装配你的Bean

    对于扫描装配而言使用的注解是 64 Component和 64 ComponentScan 64 Component是标明哪个类被扫描进入IoC容器 xff0c 而 64 ComponentScan则是标明采用何种策略去扫描装配Bean 修
  • 3.6 条件装配Bean

    Bean初始化前 xff0c 对某些属性进行校验 xff0c 满足校验才去装配数据源 为了处理这样的场景 xff0c 需要用到 64 Conditional注解 xff0c 同时需要配合另外一个接口Condition xff08 org s
  • springboot中@bean的lite模式

    当 64 Beans相互依赖时 xff0c 表示依赖关系就像一个bean方法调用另一个方法一样简 单 xff1a 64 Configuration public class AppConfig 64 Bean public Foo foo
  • 在spring boot中使用configuration注解无法注入bean

    1 在spring boot和spring中bean注入的方式 xff0c 基于Java类配置的 xff0c 即通过configuration注解注入 xff0c 其中遇到发现在springboot中 xff0c 无法自动注入 xff0c
  • 报Error creating bean with name 'dataSource' defined in class path resource 报错解决办法

    在学习spring boot 的数据库操作的时候 xff0c 报了一串错误 对于初学spring boot的我来说 xff0c 英语水平低 xff0c 看不懂报错的信息 xff0c 给我造成了很大的麻烦 xff0c 花了我一天的时间 xff
  • Error creating bean with name 'userController'

    Caused by org springframework beans factory UnsatisfiedDependencyException Error creating bean with name 39 userControll
  • Spring Autowire自动装配

    在应用中 我们常常使用
  • Spring采用properties配置多个数据库

    在一个项目中有这样的需求 上海和武汉采用不同的系统 每个系统都有自己的数据库 但是在上海的系统需要访问武汉的数据库 这就要在项目中配置两个数据源 下面是我给的SSH采用properties配置数据源的方法 1 要有两个properties文
  • 什么是JavaBean、bean? 什么是POJO、PO、DTO、VO、BO ? 什么是EJB、EntityBean?

    前言 在Java开发中经常遇到这些概念问题 有的可能理解混淆 有的可能理解不到位 特此花了很多时间理顺了这些概念 不过有些概念实际开发中并没有使用到 可能理解还不够准确 只能靠后续不断纠正了 1 什么是POJO POJO Plain Old
  • InitializingBean讲解

    InitializingBean讲解 Spring中有两种类型的Bean 一种是普通Bean 另一种是工厂Bean 即FactoryBean 工厂Bean跟普通Bean不同 其返回的对象不是指定类的一个实例 其返回的是该工厂Bean的get
  • spring_day01_IOC

    1 spring 概述 1 1 框架的概述 框架是一个封装了很多功能和模块的程序的半成品 可以理解为一个由很多工具类组成的一个工具包 1 2 spring的概念 spring是分层的JavaSE EE应用full stack轻量级开源框架
  • Quartz框架简单使用

    从此处学习到的配置方法 http www oschina net question 200745 62107 最重要的一个配置文件
  • spring+ jcaptcha(spring框架下的彩色验证码)

    从jcaptcha官方网站下载jcaptcha的发行包 并将其发行包中的jar文件考贝到本地项目WEB INF目录下的lib目录中 官方网址http jcaptcha sourceforge net 在web xml文件中配置 Java代码
  • 【Spring源码】BeanPostProcessor

    org springframework beans factory support AbstractAutowireCapableBeanFactory 八次调用时机 1 是否需要代理 resolveBeforeInstantiation
  • Srping:bean循环依赖问题

    Spring 为了解决单例的循环依赖问题 使用了 三级缓存 递归调用时发现 Bean 还在创建中即为循环依赖 单例模式的 Bean 保存在如下的数据结构中 一级缓存 用于存放完全初始化好的 bean private final Map
  • IOC的三级缓存图文详细解析(含如何解决循环依赖问题)

    1 三级缓存 1 一级缓存模型 2 二级缓存模型 3 三级缓存模型 2 解决循环依赖问题 1 三级缓存 三级缓存是为了解决循环依赖存在的 一级缓存就是储存最终的完整bean的容器 二级缓存是储存实例化但未初始化的半成品bean 三级缓存是为
  • org.springframework.beans.factory.BeanCreationException的问题所在!!

    今天一不小心弄出的小问题 严重 Exception sending context initialized event to listener instance of class org springframework web contex
  • Spring@Autowired注解与自动装配

    1 配置文件的方法 我们编写spring 框架的代码时候 一直遵循是这样一个规则 所有在spring中注入的bean 都建议定义成私有的域变量 并且要配套写上 get 和 set方法 Boss 拥有 Office 和 Car 类型的两个属性
  • JSP语法:setProperty

    JSP语法 13 setProperty 时间 2009 03 21 20 37 来源 作者 CSDN IE QQ 百度 Google POCO 新浪 365Key 天极 和讯 博拉 Live 奇客 收客 饭否 叽歪
  • @Configuration和@Bean

    spring Boot提倡约定优于配置 如何将类的生命周期交给spring 1 第一种自己写的类 Controller Service 用 controller service即可 2 第二种 集成其它框架 比如集成shiro权限框架 集成

随机推荐

  • 利用阿里云下载国外镜像,国内顺畅下载k8s.gcr.io的镜像

    国内顺畅下载k8s gcr io的镜像 1 起因 配置kube dns是3个k8s gcr io的镜像无法下载 报错如下 Error response from daemon Get https k8s gcr io v2 net http
  • pip 使用阿里源

    pip 使用阿里源 使用pip install 的时候默认会去国外服务器下载 所以经常断开或者速度很慢 只需要在原来的命令后加上 i https mirrors aliyun com pypi simple即可直接从阿里源上安装 pip s
  • sun.misc包找不到

    转 http blog csdn net jbxiaozi article details 7351768 1 右键项目 属性 java bulid path jre System Library access rules resoluti
  • npm安装vue报错:npm ERR! code ETIMEDOUT

    npm安装vue报错 信息如下 C span class token punctuation span Users span class token punctuation span Q span class token operator
  • 将element-plus分页组件由默认英文,改为中文

    1 现象 分页组件默认显示为英文 但实际页面中大多都是中文 弄个英文显得比较突兀 2 配置 在main js中添加以下两句语句 span class token function import span locale from span c
  • [Gitops--2]Argocd和Gitlab-runner安装配置

    ArgoCd Argo是一组k8s原生工具集 用于运行和管理k8s上的作业和应用程序 Argo提供了一种在k8s上创建工作和应用的三种计算模式 服务模式 工作流模式和基于事件模式 所有的Argo工具都实现为了创建控制器和自定义资源 为什么选
  • Windows update 0x8024401c 0x80244019

    Windows 更新失败 报错 0x8024401c 0x80244019 以系统管理员身份运行 net stop wuauserv reg delete f HKEY LOCAL MACHINE span class token punc
  • K8s常见面试题20问

    K8s常见面试题19问 收集了一些K8s常见问题和同学们面试常被问到的问题 如果有新的面试题私聊或者留言给我 1 Docker和虚拟机有那些不同 虚拟化环境下每个 VM 是一台完整的计算机 xff0c 在虚拟化硬件之上运行所有组件 xff0
  • Dockerfile常用命令

    Dockerfile常用命令 1 Dockerfile Dockerfile是一个文本文件 用一组指令来完成镜像的构建 每一条指令构建一层镜像 所有尽量将相同的命令合并成一行以减少中间镜像的层数 2 From 必须 指定基础镜像即我从哪里可
  • Kubesphere流水线实现蓝绿发布

    Kubesphere流水线实现蓝绿发布 1 Gitlab仓库准备 1 1 创建仓库 新建空白项目 名字随便取 greenweb 复制克隆地址 http 192 168 31 199 deploy greenweb git 1 2 初始化并上
  • 【NetWorkX实例(3)】图、边、节点等相关方法

    更全面的NetworkX中文使用手册 xff0c 请收藏 xff1a NetworkX中文使用手册 在 NetWorkX实例 1 基础操作一文中 xff0c 介绍了networkx中图的生成 xff0c 下面就介绍一下图 边 节点等相关方法
  • Python调用外部EXE程序遍历窗体及控件并获取控件信息。

    背景 我的工作中经常手工运行一个windows程序 xff08 密码生成工具 xff09 xff0c 获取该程序的计算结果 xff0c 手工填到登录表单的中 该程序非常久远 xff0c 已无人维护 根据凡是重复2次以上的工作都应该自动化原则
  • J-Link RTT Viewer使用教程(附代码)

    目录 RTT Real Time Transfer 简介 使用教程 常用API介绍 RTT缓冲大小修改 使用printf重定向 官方例程 RTT Real Time Transfer 简介 平常调试代码中使用串口打印log xff0c 往往
  • [问题记录]JNI的整型数组返回出现stack corruption

    问题记录 JNI的整型数组返回出现stack corruption 在项目中编写了一个返回整型数组的JNI代码 xff0c 但是在测试时发现问题 xff0c 会产生stack corruption错误 xff0c debug之后发现是ret
  • Android逆向系列--JDWP协议

    Android逆向系列 JDWP协议 背景简介使用源码调用参考 背景 经常会遇到各种各样需要使用jdwp知识的场景 xff0c 比如调试Java源码 比如抓帧等等 xff0c 这些关联知识点通常都会极其复杂 xff0c 如果不能很好的了解j
  • 银河麒麟V10桌面版系统将用户开发Qt界面程序添加为开机自启动

    银河麒麟V10桌面版系统将用户开发Qt界面程序添加为开机自启动 银河麒麟V10桌面版系统允许用户开发自己的qt界面程序并将其添加为开机自启动 这样 xff0c 每次开机后 xff0c 用户开发的qt界面程序会自动启动 xff0c 无需手动打
  • 环境搭建-Linux-Mysql安装-10.3.7-MariaDB-log MariaDB Server

    10 3 7 MariaDB log MariaDB Server 安装记录 linux 系统 CentOS7 无脑安装 sudo yum install y redhat lsbsudo yum install y net tools关闭
  • 200506--iOS之NSAttributedString类

    Class NSAttributedString A string that has associated attributes such as visual style hyperlinks or accessibility data f
  • 批处理文件(bat)之全彩滚动我爱你

    前言 xff1a 本文章分享利用bat文件制作炫彩的全屏滚动文字效果 xff0c 具体效果可关注我的抖音 xff0c 查看短视频介绍 代码实现 xff1a 64 echo off amp setlocal enabledelayedexpa
  • @Bean放入其引用Bean中初始化失败分析

    以下讨论的问题及术语均在SpringBoot框架下 xff0c 问题十分小众 xff0c 仅做整理记录 1 先说重点 Bean依赖属性的注入顺序 xff0c 与代码定义顺序无关 xff1b 最好是将 64 Bean注解配置的Bean放在 6