Spring中的上下文工具你写的可能有bug

文章目录

  • 前言
  • 功能
    • 第一种:ApplicationContext
    • 第二种方式:ApplicationContextAware
    • 第三种:BeanFactoryPostProcessor
  • 源码
    • 第一种
    • 第二种
    • 第三种

前言

本篇是针对如何写一个比较好的spring工具的一个探讨。

功能

下面三种方式,你觉得哪种最好?

  1. 第一种:直接注入ApplicationContext
  2. 第二种:实现ApplicationContextAware接口;
  3. 第三种:实现BeanFactoryPostProcessor接口;

第一种:ApplicationContext

它的功能如下,它有国际化功能,beanFactory功能,事件发布功能,以及资源加载功能,作为上下文,他这个功能已经很强大了。

它发生的时机是在bean实例化后的依赖注入。

image-20231223133451041

示例:

@Component
public class CustomConfig9 {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @PostConstruct
    public void init() {
        CustomConfig3 bean = applicationContext.getBean(CustomConfig3.class);
        System.out.println("customConfig9 获取了 customConfig3" + bean);
    }
}

image-20231223141902132

优点: 这种方式也比较简单,在需要用的bean中直接注入就行;

缺点: 它的局限就是在bean中才能使用,如果你要给工具类,或一个静态方法中使用,你就不太好这样做,你得控制你业务的执行时机;

第二种方式:ApplicationContextAware

这种方式是通过Bean初始化后,执行Aware接口回调方式实现,我见过很多项目,他们都是这样做的:

@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtil.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

这写法没毛病,可是,它存在一个bug :不是任何地方都能使用。

为何这么说?

那么我再增加一个类:

@Component
public class CustomConfig6 implements InitializingBean {

    private CustomConfig config;

    @Override
    public void afterPropertiesSet() throws Exception {
        config = SpringUtil.getBean(CustomConfig.class);
        // 这里示例比较简单,一般业务场景可能是jdbc检索,redis缓存这些逻辑
    }
}

在我增加了这样的一个类后,你觉得你的项目会是正常的吗?

请思考3秒钟…

.

.

.

那么答案是有可能是异常的,为何?

大家还记得Aware接口是在哪个时机调用的,它是在bean初始化后调用的,spring bean的生命周期是单线程的,如果说Spring先实例化了CustomConfig6,那么它会先调用afterPropertiesSet里的SpringUtil.getBean,而这时SpringUtil还没有被实例化,SpringUtil里的applicationContext必然是null

为何会有先后顺序?

我们先复习一下Spring怎么扫描bean的,Spring是先扫描的当前包下的class,顺序扫描,扫描到的class,在经过一些了的校验后,会放到一个容器里,实例化时,再根据bean的名称(它是一个list)进行遍历实例化,到这,大概的一个原因应该明了了吧,如果SpringUtil所在的文件位置考前,在其他类之前扫描到,就能先实例化,那么就是正常的,如果它靠后,就会出现其他业务bean回调时,通过SpringUtil使用ApplicationContext功能而出现空指针异常。

优点: 使用时直接静态方法调用,方便;

缺点: 可能存在bug;

第三种:BeanFactoryPostProcessor

在第二种的方式上进行优化,我们需要考虑,它的一个初始化时机,bean实例化都是统一进行的,所以,我们要打破这个规则,提前进行对SpringUtil中的applicationContext进行赋值,所以我们可以使用BeanFactoryPostProcessor,这个后置处理器是在beanFactory准备完成后端一个回调操作,我们的bean,配置类等等这些都是在这里被扫描出来的,是bean生命周期开始的开端。

@Component
public class SpringUtil2 implements BeanFactoryPostProcessor {

    private static ConfigurableListableBeanFactory applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtil2.applicationContext = beanFactory;
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

优点: 可用在任何地方法;

源码

第一种

ApplicationContext它是通过依赖注入进行注入的,我们直接看创建bean的方法

位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

image-20231223152332165

那我们的ApplicationContext就是在populateBean方法中被注入点,但是在此之前,它需要查找注入点,然后在注入时,可以直接通过注入点进行属性注入。(图片没有写全,注入点包含@Value, @Inject, 依赖注入的也包含@Value这写, 详细的看:spring源码篇(四)依赖注入(控制反转))

@Autowired注入是由AutowiredAnnotationBeanPostProcessor后置处理器进行处理,而@ResourceCommonAnnotationBeanPostProcessor处理

第二种

第二种是Aware方法调用,也是在bean初始化时调用的,如上面图片描述的,他会在initializeBean方法中调用,位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

image-20231223154215448

image-20231223153523930

invokeAwareMethods它提供了3个Aware

  • BeanNameAware:回调setBeanName,实际上调用有我们控制,你想做什么就做什么;
  • BeanClassLoaderAware:bean容器的类加载器,通过它你可以加载到classpath(这个包含多种路径)下的所有class;
  • BeanFactoryAware:bean工厂(bean容器)回调,

其他的在ApplicationContextAwareProcessor

image-20231223154321603

如上,只要你实现ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware他都会吧applicationContext给你设置上。

第三种

这第三种Spring启动时执行的一个方法,就是进行bean容器的初始化;

这就是我们main方法里的SpringApplication.run

image-20231223154713386

image-20231223155058597

image-20231223155258481

这部分就是执行我们自定义的beanFactoryPostProcessor,它分排序的和没有排序的,这个方法已经来会先进行BeanDefinitionRegistryPostProcessor这个处理器的执行,这里就是进行扫描,解析配置类和bean(@Component, @Configuation, @Bean....)的地方。所以我们在后面才能获取的我们自定义的bean,并提前实例化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/266692.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第1课 配置FFmpeg+OpenCV开发环境

一、配置开发环境 1.下载FFmpegOpenCV开发所用的SDK压缩包&#xff0c;并解压到E:\SDK下&#xff0c;解压后的路径应为&#xff1a;E:\SDK\ffmpeg-sdk\58\x86\dll及E:\SDK\opencv-sdk\340\x86\dll。 2.新建VC项目&#xff0c;名称为demo1&#xff0c;项目类弄为MFC应用程序&a…

Django 中集成 CKEditor 富文本编辑器详解

概要 在 Web 应用中&#xff0c;富文本编辑器是提高用户体验的重要组件之一。CKEditor 是一款流行的、功能丰富的富文本编辑器。在 Django 项目中集成 CKEditor 不仅可以提升内容编辑的灵活性&#xff0c;还能丰富用户的互动体验。本文将详细介绍如何在 Django 中集成和配置 C…

掌握函数式组件:迈向现代化前端开发的关键步骤(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【pynput】鼠标行为追踪并模拟

文章目录 前言基本思路安装依赖包实时鼠标捕获捕获鼠标位置捕获鼠标事件记录点击内容 效果图 利用本文内容从事的任何犯法行为和开发与本人无关&#xff0c;请理性利用技术服务大家&#xff0c;创建美好和谐的社会&#xff0c;让人们生活从繁琐中变得更加具有创造性&#xff01…

【SassVue】仿网易云播放器动画

简介 仿网易云播放动画 效果图&#xff08;效果图&#xff09; 最终成品效果 动画组件 src/components/musicPlay.vue <template><div class"music-play"><div></div><div></div><div></div></div> </te…

【JAVA】分布式链路追踪技术概论

目录 1.概述 2.基于日志的实现 2.1.实现思想 2.2.sleuth 2.2.可视化 3.基于agent的实现 4.联系作者 1.概述 当采用分布式架构后&#xff0c;一次请求会在多个服务之间流转&#xff0c;组成单次调用链的服务往往都分散在不同的服务器上。这就会带来一个问题&#xff1a;…

基于Kubernetes的jenkins上线

1、基于helm 部署jenkins 要求&#xff1a;当前集群配置了storageClass&#xff0c;并已指定默认的storageClass&#xff0c;一般情况下&#xff0c;创建的storageClass即为默认类 指定默认storageClass的方式 # 如果是新创建默认类&#xff1a; apiVersion: storage.k8s.io/v1…

全方位掌握卷积神经网络:理解原理 优化实践应用

计算机视觉CV的发展 检测任务 分类与检索 超分辨率重构 医学任务 无人驾驶 整体网络架构 卷积层和激活函数&#xff08;ReLU&#xff09;的组合是网络的核心组成部分 激活函数(ReLU&#xff09; 引入非线性&#xff0c;增强网络的表达能力。 卷积层 负责特征提取 池化层…

go语言初体验1--使用go install

当安装后go语言后。 尝试编写go程序。 当使用 go install 命令&#xff0c;报错。 go: go install requires a version when current directory is not in a moduleTry go install jvmgo\ch01latest to install the latest version通过查找资料。 用命令&#xff1a; go env …

数据库开发之图形化工具以及表操作的详细解析

2.3 图形化工具 2.3.1 介绍 前面我们讲解了DDL中关于数据库操作的SQL语句&#xff0c;在我们编写这些SQL时&#xff0c;都是在命令行当中完成的。大家在练习的时候应该也感受到了&#xff0c;在命令行当中来敲这些SQL语句很不方便&#xff0c;主要的原因有以下 3 点&#xff…

Centos安装vsftpd:centos配置vsftpd,ftp报200和227错误

一、centos下载安装vsftpd&#xff08;root权限&#xff09; 1、下载安装 yum -y install vsftpd 2、vsftpd的配置文件 /etc/vsftpd.conf 3、备份原来的配置文件 sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.backup 4、修改配置文件如下&#xff1a;vi /etc/vsftpd.conf …

Hadoop入门学习笔记——三、使用HDFS文件系统

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 三、使用HDFS文件系统3.1. 使用命令操作HDFS文件系统3.1.…

语法分析树(先看例子再看定义)

语法分析树&#xff08;先看例子再看定义&#xff09; 先讲例子 书上讲问题&#xff0c;先讲定义&#xff0c;一顿学术操作&#xff0c;让人云里雾里&#xff0c;然后出例子。其实这样往往让人觉得看书的过程就是放弃的过程。 关于语法分析树&#xff0c;我先从上篇文章的例…

新版IDEA中Git的使用(一)

说明&#xff1a;本文介绍如何在新版IDEA中使用Git 创建项目 首先&#xff0c;在GitLab里面创建一个项目&#xff08;git_demo&#xff09;&#xff0c;克隆到桌面上。 然后在IDEA中创建一个项目&#xff0c;项目路径放在这个Git文件夹里面。 Git界面 当前分支&Commit …

python dash 写一个登陆页 4

界面 代码&#xff1a; 这里引入了dash_bootstrap_components 进行界面美化 &#xff0c;要记一些className&#xff0c;也不是原来说的不用写CSS了。 from dash import Dash, html, dcc, callback, Output, Input, State import dash_bootstrap_components as dbcapp Dash(…

使用 KVM 管理程序优化虚拟化

KVM&#xff08;基于内核的虚拟机&#xff09;是一项强大的开源虚拟化技术&#xff0c;内置于Linux 内核。它支持在单个物理主机上运行多个虚拟机 (VM)&#xff0c;这对于资源效率、服务器整合以及为不同目的创建隔离环境特别有帮助。 本文将深入介绍 KVM 管理程序&#xff0…

MySQL主从架构及读写分离实战

​​​​​​ 目录 一、实验目的与环境 二、基础环境介绍 三、搭建主从集群 1、理论基础 2、同步的原理 3、搭建主从集群 3.1 配置master主服务器 3.2 配置slave从服务 3.3 主从集群测试 3.4 集群搭建扩展&#xff1a; 3.5、GTID同步集群 4、集群扩容 5、半同步复…

Git的总体认知与具体实现

GIt概念 是一种分布式控制管理器 tips:敏捷开发 -> 先上线&#xff0c;后续开发再继续开发 集中式和分布式 集中式的版本控制系统每次在写代码时都需要从服务器中拉取一份下来&#xff0c;并且如果服务器丢失了&#xff0c;那么所有的就都丢失了&#xff0c;你本机客户端仅…

电力系统风储联合一次调频MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 简介&#xff1a; 同一电力系统在不同风电渗透率下遭受同一负荷扰动时&#xff0c;其频率变化规律所示&#xff1a; &#xff08;1&#xff09;随着电力系统中风电渗透率的不断提高&#xff0c;风电零惯性响…

要参加微软官方 Copilot 智能编程训练营了

GitHub Copilot 是由 GitHub、OpenAI 和 Microsoft 联合开发的生成式 AI 模型驱动的。 GitHub Copilot 分析用户正在编辑的文件及相关文件的上下文&#xff0c;并在编写代码时提供自动补全式的建议。 刚好下周要参加微软官方组织的 GitHub Copilot 工作坊-智能编程训练营&…