SpringBoot静态类调用Bean的两种方案(新手版)

 更新时间:2025年12月05日 09:48:47   作者:Dolphin_Home  
这篇文章主要介绍了两种解决Spring Boot中静态类调用Bean问题的方法:方案一使用SpringContextHolder(中间人模式),方案二使用@PostConstruct注解(入住模式),根据具体需求选择合适的方案,需要的朋友可以参考下

一、开篇痛点:静态类调用Bean,小白必遇的“拦路虎”

刚接触SpringBoot的同学,大概率会碰到这个问题:
写了个静态工具类(比如 MyStaticUtils),想在静态方法里调用Spring管理的Bean(比如 UserService),用 @Autowired 直接注入却报 NullPointerException

比如想实现“根据用户ID查用户名”的静态方法,直接这么写必错:

// 错误示范
public class MyStaticUtils {
    @Autowired
    private static UserService userService; // 注入失败,永远是null

    public static String getUserNameById(Long userId) {
        return userService.getUserName(userId); // 运行时空指针
    }
}

核心原因还是之前说的:静态类加载时机早于Spring Bean初始化@Autowired 是给Spring实例注入Bean的,管不到静态变量。

今天就给大家讲两种解决方案,重点对比侵入性差异,帮小白按需选择!

二、两方案侵入性对比(一眼看懂区别)

先上核心对比表,小白不用记复杂逻辑,看表格就知道差异:

对比维度方案一:SpringContextHolder(中间人模式)方案二:@PostConstruct注解(入住模式)
核心思路用中间类持有Spring上下文,静态类按需获取Bean让静态类被Spring管理,初始化后给静态变量赋值
代码侵入性低(静态类无需加Spring注解,保持纯静态特性)高(静态类需加@Component,依赖Spring注解)
实现复杂度中等(需新增1个中间类)简单(无需新增类,仅改静态工具类)
静态类独立性强(可脱离Spring单独使用)弱(必须被Spring扫描管理,否则失效)
适用场景不想修改静态类结构、侵入性要求低的场景静态类可被Spring管理、逻辑简单的场景

简单说:方案一是“找中间人拿工具”,方案二是“让静态类住进Spring的房子里直接拿工具”。

三、方案一:SpringContextHolder(低侵入,通用方案)

1. 核心逻辑

通过一个“中间人”(SpringContextHolder)持有Spring的上下文(ApplicationContext),静态类需要Bean时,直接问“中间人”要,不用自己依赖Spring。

2. 快速实现(简要回顾,重点看对比)

// 1. 中间人:SpringContextHolder(直接复制可用)
@Component
public class SpringContextHolder implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        SpringContextHolder.applicationContext = context;
    }

    // 静态方法:获取Bean
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

// 2. 静态工具类调用(无需加任何Spring注解)
public class MyStaticUtils {
    public static String getUserNameById(Long userId) {
        // 关键:通过中间人获取Bean
        UserService userService = SpringContextHolder.getBean(UserService.class);
        return userService.getUserName(userId);
    }
}

3. 核心优势

静态类还是“纯静态”,不用加 @Component,就算脱离Spring环境也能单独使用,侵入性极低。

四、方案二:@PostConstruct注解(高侵入,简单方案)

这就是你找到的CSDN方案,核心是“让静态类被Spring管理”,通过注解在Bean初始化后给静态变量赋值,巧妙但侵入性强。

1. 核心原理(小白通俗理解)

  • 给静态工具类加 @Component,让它变成Spring管理的Bean(住进Spring的“房子”);
  • @Autowired 先给实例变量注入Bean(Spring能管理实例变量);
  • @PostConstruct 注解一个初始化方法,在Spring初始化这个工具类后,把实例变量的值赋给静态变量;
  • 静态方法直接用静态变量调用Bean,就不会空指针了。

2. 分步实现(还是用“查用户名”案例)

步骤1:创建Spring管理的Bean(UserService)

和之前一致,模拟查询逻辑:

@Service
public class UserService {
    // 模拟根据ID查用户名
    public String getUserName(Long userId) {
        return userId == 1 ? "张三" : "未知用户";
    }
}

步骤2:改造静态工具类(核心改造)

给工具类加 @Component,配合 @Autowired@PostConstruct

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

// 关键1:加@Component,让Spring管理这个工具类
@Component
public class MyStaticUtils {
    // 关键2:先注入实例变量(Spring能识别@Autowired)
    @Autowired
    private UserService userService;

    // 关键3:静态变量(供静态方法使用)
    private static MyStaticUtils staticUtils;

    // 关键4:@PostConstruct:Spring初始化当前Bean后执行(初始化顺序:构造方法→@Autowired→@PostConstruct)
    @PostConstruct
    public void init() {
        // 把当前实例赋值给静态变量
        staticUtils = this;
    }

    // 静态方法:通过静态变量调用Bean的方法
    public static String getUserNameById(Long userId) {
        // 核心:staticUtils持有实例,实例持有注入的userService
        return staticUtils.userService.getUserName(userId);
    }
}

步骤3:测试验证

写个Controller调用:

@RestController
public class TestController {
    @GetMapping("/user/{userId}")
    public String getUserName(@PathVariable Long userId) {
        return MyStaticUtils.getUserNameById(userId); // 调用静态方法
    }
}

启动项目访问 http://localhost:8080/user/1,返回“张三”—— 成功!

3. 原理拆解(小白必懂)

用“房子”比喻再讲一遍:

  1. @ComponentMyStaticUtils 住进了Spring的“房子”,成为Spring管理的Bean;
  2. Spring初始化时,先执行 @Autowired,给实例变量 userService 注入Bean(相当于给房子里放了工具);
  3. 再执行 @PostConstruct 标注的 init() 方法,把当前实例(this)赋给静态变量 staticUtils(相当于给房子配了把“静态钥匙”);
  4. 静态方法通过 staticUtils 这把钥匙,就能拿到房子里的 userService 工具了。

五、方案二关键注意事项(小白避坑)

  1. 必须加 @Component:否则Spring不会管理这个工具类,@Autowired@PostConstruct 都无效,静态变量还是null;
  2. 实例变量+静态变量配合:不能直接给静态变量加 @Autowired(Spring不支持),必须先注入实例变量,再通过 @PostConstruct 赋值给静态变量;
  3. @PostConstruct 执行时机:在构造方法之后、Bean初始化完成之前执行,只会执行一次,确保静态变量只赋值一次;
  4. 不能在静态代码块中调用:静态代码块加载时机早于 @PostConstruct,此时 staticUtils 还没赋值,调用会空指针。

六、适用场景总结(小白怎么选?)

场景描述推荐方案理由
静态工具类不想被Spring管理、需保持独立性方案一:SpringContextHolder侵入性低,工具类可脱离Spring使用
静态工具类仅在Spring环境中使用、逻辑简单方案二:@PostConstruct无需新增中间类,代码量少,实现简单
项目中静态工具类较多、需统一管理方案一:SpringContextHolder中间类可复用,维护成本低
不想新增额外类、快速实现需求方案二:@PostConstruct直接改造工具类,几分钟就能搞定

七、最终建议(小白牢记)

  1. 如果你是“纯小白”,只是想快速实现需求,且静态工具类只在Spring项目中用,选方案二(@PostConstruct),代码简单不用记中间类;
  2. 如果你想让代码更规范、侵入性更低,或者静态工具类可能脱离Spring使用,选方案一(SpringContextHolder),通用性更强;
  3. 两种方案的核心都是“解决静态类和Spring Bean加载时机不匹配的问题”,只是实现思路不同,没有绝对优劣,按需选择即可。

其实这两种方案本质是“取舍”:方案二用“侵入性”换“简单性”,方案一用“多一个中间类”换“低侵入性”。学会这两种,以后遇到静态类调用Bean的问题,再也不用一头雾水啦!

以上就是SpringBoot静态类调用Bean的两种方案(新手版)的详细内容,更多关于SpringBoot静态类调用Bean的资料请关注脚本之家其它相关文章!

相关文章

  • feign客户端设置超时时间操作

    feign客户端设置超时时间操作

    这篇文章主要介绍了feign客户端设置超时时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • java集合中HashSet LinkedHashSet TreeSet三者异同面试精讲

    java集合中HashSet LinkedHashSet TreeSet三者异同面试精讲

    这篇文章主要为大家介绍了java集合中HashSet LinkedHashSet TreeSet三者异同面试精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Java 求解如何把二叉搜索树转换为累加树

    Java 求解如何把二叉搜索树转换为累加树

    这篇文章主要介绍了Java 求解把二叉搜索树转换为累加树的代码,总之需要观察示例节点的规律,需要记录上个节点的情况,注意引入前驱节点pre,具体实例代码跟随小编一起看看吧
    2021-11-11
  • 浅析Java中的SPI原理

    浅析Java中的SPI原理

    SPI:由调用方制定接口标准,实现方来针对接口提供不同的实现,SPI其实就是"为接口查找实现"的一种服务发现机制。本文将浅谈一下SPI机制的原理,需要的可以参考一下
    2022-09-09
  • Sublime Text 打开Java文档中文乱码的解决方案

    Sublime Text 打开Java文档中文乱码的解决方案

    这篇文章主要介绍了Sublime Text 中文乱码的解决方案,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-12-12
  • 基于Spring-cloud-gateway实现全局日志记录的方法

    基于Spring-cloud-gateway实现全局日志记录的方法

    最近项目在线上运行出现了一些难以复现的bug需要定位相应api的日志,通过nginx提供的api请求日志难以实现,于是在gateway通过全局过滤器记录api请求日志,本文给大家介绍基于Spring-cloud-gateway实现全局日志记录,感兴趣的朋友一起看看吧
    2023-11-11
  • 如何使用Spring Boot ApplicationRunner解析命令行中的参数

    如何使用Spring Boot ApplicationRunner解析命令行中的参数

    这篇文章主要介绍了使用Spring Boot ApplicationRunner解析命令行中的参数,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-12-12
  • java如何导出insert语句并生成sql脚本

    java如何导出insert语句并生成sql脚本

    这篇文章主要介绍了java导出insert语句并生成sql脚本的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java读取传输FTP文件实现示例

    Java读取传输FTP文件实现示例

    本文主要介绍了Java读取传输FTP文件方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • SpringCloud Gateway DispatcherHandler调用方法详细介绍

    SpringCloud Gateway DispatcherHandler调用方法详细介绍

    我们第一个关注的类就是DispatcherHandler,这个类提供的handle()方法,封装了我们之后所有的handlerMappings,这个DispatcherHandler有点想SpringMVC的DispatchServlet,里面也是封装了请求和对应的处理方法的关系
    2022-10-10

最新评论