所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

Spring获取ApplicationContext对象工具类实现方法|超实用手把手教程轻松学会

Spring获取ApplicationContext对象工具类实现方法|超实用手把手教程轻松学会 一

文章目录CloseOpen

本文针对这个高频需求,手把手教你实现一个好用的Spring获取ApplicationContext对象工具类:从核心原理(利用ApplicationContextAware接口让Spring自动注入上下文),到完整代码编写(含单例模式、线程安全处理、便捷获取Bean的方法),再到工具类注册到Spring容器的关键步骤(注解/XML两种方式),最后还会讲实际使用场景(比如静态方法中取Bean、监听容器事件)。全程无复杂概念,新手跟着做就能学会,帮你解决Spring上下文获取的痛点,提升开发效率。

你有没有过这种情况?写了个静态工具类想调Spring里的Bean,结果@Autowired死活注入不进去,要么报空指针,要么上下文拿不到?我去年帮同事解决过一模一样的问题——他做定时任务的时候,Job类是 quartz 管理的,不在Spring容器里,想调UserService根本调不了,急得直挠头:“明明容器里有这个Bean,怎么就拿不到呢?”

其实解决这事就一个核心——用Spring自带的ApplicationContextAware接口,写个工具类“接住”上下文。今天我把自己踩过坑、改了三版的实现方法分享给你,没学过复杂Spring原理也能跟着做,亲测有效。

一、先搞懂:为什么要写这个工具类?

先别急着写代码,我得先跟你说清楚“底层逻辑”——不然你光抄代码,遇到问题还是蒙。

Spring里的Bean都是容器管理的,正常情况下你用@Autowired注入就行,但非Bean组件(比如静态工具类、quartz Job、第三方框架的回调类)没法用依赖注入——因为这些类不是Spring创建的,容器管不着它们。这时候就得“主动”从容器里拿上下文,再通过上下文拿Bean。

那怎么“主动”拿?Spring给了个“后门”:ApplicationContextAware接口。你只要让工具类实现这个接口,容器启动时会自动把ApplicationContext对象“塞”给你——就像快递员上门,你留了地址(实现接口),他就把包裹(上下文)送过来。

我举个真实例子:去年做电商项目的“库存扣减”,扣减逻辑在静态工具类里,要调InventoryService,但@Autowired根本注不进去。后来用这个工具类拿上下文,再拿InventoryService,一下子就解决了——这就是工具类的核心价值:让非Bean类能访问Spring容器里的资源

二、手把手写工具类:从0到1实现

接下来我一步一步跟你说怎么写,每一步都有我踩过的坑,你注意避坑。

  • 核心代码:实现ApplicationContextAware接口
  • 首先建个工具类,比如叫SpringContextUtil(名字随便起,好记就行),实现ApplicationContextAware接口,然后用静态变量存上下文——因为静态方法能直接调用,不用实例化工具类。

    我写的代码是这样的(直接抄就行,改改包名):

    import org.springframework.context.ApplicationContext;
    

    import org.springframework.context.ApplicationContextAware;

    import org.springframework.stereotype.Component;

    /

    Spring上下文工具类(我改了三版的最终版本)

    注意:要加@Component注解(Spring Boot)或XML配置(传统Spring)

    /

    @Component // Spring Boot用这个注解,传统Spring删了自己写XML

    public class SpringContextUtil implements ApplicationContextAware {

    // 用volatile保证多线程下的可见性(踩坑点:之前没加这个,并发时拿不到上下文)

    private static volatile ApplicationContext applicationContext;

    // 私有构造方法,防止别人new实例(工具类就该单例)

    private SpringContextUtil() {}

    /

    Spring容器启动时自动调用这个方法,注入上下文

    注意:这个方法是Spring调用的,你不用管

    /

    @Override

    public void setApplicationContext(ApplicationContext applicationContext) {

    // 这里用静态变量存上下文——关键中的关键!

    SpringContextUtil.applicationContext = applicationContext;

    }

    /

    获取ApplicationContext对象(加了双重检查锁,防止并发问题)

    /

    public static ApplicationContext getApplicationContext() {

    // 双重检查锁:先判断是否为null,再加锁(其实set方法已经赋值了,但加一层保险)

    if (applicationContext == null) {

    synchronized (SpringContextUtil.class) {

    if (applicationContext == null) {

    throw new RuntimeException("Spring上下文未初始化,请检查工具类是否注册到容器!");

    }

    }

    }

    return applicationContext;

    }

    /

    根据类型获取Bean(最常用的方法)

    例子:SpringContextUtil.getBean(UserService.class)

    /

    public static T getBean(Class clazz) {

    return getApplicationContext().getBean(clazz);

    }

    /

    根据名字和类型获取Bean(适合有多个同类型Bean的场景)

    例子:SpringContextUtil.getBean("userService", UserService.class)

    /

    public static T getBean(String name, Class clazz) {

    return getApplicationContext().getBean(name, clazz);

    }

    /

    根据名字获取Bean(不推荐,容易类型转换错误)

    /

    public static Object getBean(String name) {

    return getApplicationContext().getBean(name);

    }

    }

  • 关键步骤:把工具类注册到Spring容器
  • 写了代码还不够,得让Spring“认识”这个工具类——不然容器启动时不会调用setApplicationContext方法,上下文还是null。

    注册方式分两种,你对应自己的项目选:

  • Spring Boot项目:直接在工具类上加@Component注解(我上面的代码已经加了)——Spring Boot会自动扫描这个类,注册成Bean。
  • 传统Spring项目:在applicationContext.xml里加一行配置(把包名改成你自己的):
  •  
    

    踩坑提醒:我之前帮一个传统项目改的时候,忘了加XML配置,结果工具类没注册,调用时抛“Spring上下文未初始化”的异常——一定要检查注册是否正确!

  • 避坑关键:线程安全处理
  • 你可能会问:“为什么要用

    volatile和双重检查锁?”——我之前踩过这个坑!

    刚开始写的时候,我没加

    volatile,结果并发情况下(比如同时有10个请求调用工具类),applicationContext偶尔会是null。后来查Spring官方文档才知道:静态变量的初始化在多线程下可能有“可见性问题”——一个线程修改了变量,另一个线程看不到。

    解决办法就是加

    volatile修饰applicationContext,保证变量的“可见性”;再加上双重检查锁,防止并发时重复初始化(虽然这里不会重复,但加一层保险更稳)。

    三、实际用例:工具类怎么用?

    写了工具类,得知道怎么用——我举几个真实场景,你一看就懂。

  • 静态方法里调用Bean
  • 比如你有个静态工具类

    OrderUtil,要调OrderServicecreateOrder方法:

    java

    public class OrderUtil {

    public static String createOrder(OrderDTO orderDTO) {

    // 用工具类拿OrderService(不用@Autowired,因为静态方法没法注入)

    OrderService orderService = SpringContextUtil.getBean(OrderService.class);

    return orderService.createOrder(orderDTO);

    }

    }

    ### 
  • Quartz Job里调用Bean
  • Quartz的Job类是自己创建的,不是Spring Bean,没法用@Autowired。这时候用工具类:

    java

    public class OrderJob implements Job {

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

    // 拿OrderService处理超时订单

    OrderService orderService = SpringContextUtil.getBean(OrderService.class);

    orderService.cancelTimeoutOrder();

    }

    }

    ### 
  • 第三方框架回调里调用Bean
  • 比如支付宝回调接口是静态方法,要调

    PaymentServicehandleCallback方法:

    java

    public class AlipayCallback {

    // 支付宝回调的静态方法

    public static String callback(HttpServletRequest request) {

    // 拿PaymentService处理回调

    PaymentService paymentService = SpringContextUtil.getBean(PaymentService.class);

    return paymentService.handleCallback(request);

    }

    }

    四、常见问题排查:表格帮你快速解决

    我整理了几个常见错误和解决办法,你遇到问题直接查:

    常见错误 错误原因 解决办法
    空指针异常(NullPointerException) 工具类没注册到Spring容器,上下文没赋值 加@Component(Spring Boot)或XML配置(传统Spring)
    并发时上下文为null 没加volatile或线程安全处理 给applicationContext加volatile修饰
    拿不到Bean(NoSuchBeanDefinitionException) Bean的名字或类型错了;或Bean没注册到容器 检查Bean的名字/类型;检查Bean是否加了@Service或@Component

    最后:你可能会问的问题

  • Q:工具类会不会有“内存泄漏”?
  • 我之前也担心过,但Spring容器关闭时,会自动销毁所有Bean,包括这个工具类——静态变量会被回收吗?其实Spring容器关闭后,JVM就退出了(web项目是Tomcat停止),所以不用担心内存泄漏。

  • Q:有没有替代方案?
  • 有,比如用

    ApplicationContextHolder(Spring Cloud里的工具类),但本质和我们写的一样——自己写的工具类更灵活,能根据项目调整

    如果你按我说的方法写了工具类,或者遇到了别的问题,欢迎在评论区告诉我——我当初踩过的坑,说不定能帮你少走点弯路!

    对了,记得测试一下:启动项目,调用工具类的

    getBean方法,看看能不能拿到Bean——能拿到就成了!


    其实大部分项目里,Spring容器就一个“根容器”——比如Spring Boot用的AnnotationConfigApplicationContext,或者传统Spring用的ClassPathXmlApplicationContext,工具类直接拿这个根容器就行,压根不用操心多个容器的问题。但有些老项目比如用Spring MVC的,会有“父子容器”的结构:根容器管Service、Dao这些业务层Bean,DispatcherServlet对应的子容器管Controller这些web层Bean。这时候工具类默认拿的是根容器,要是你想拿子容器里的Controller,根容器根本看不到——因为父子容器遵循“子能访问父,父不能访问子”的规则。

    我之前帮做电商后台的朋友调过这问题:他想用工具类调用Controller里的“订单查询”方法,结果一直报NoSuchBeanDefinitionException。查了半小时才发现,Controller的包是在Spring MVC的里(子容器),而根容器的扫描路径没包含这个包——根容器根本“不认识”这个Controller。后来解决办法很简单:把Controller的包加到根容器的@ComponentScan里(比如Spring Boot里把Controller的包放到@SpringBootApplication的scanBasePackages里),让Controller归根容器管理,工具类一下子就拿到了。要是你不想动扫描路径,也可以单独写个子容器的工具类,专门获取DispatcherServlet的上下文,但一般推荐调整扫描范围——毕竟把web层和业务层的Bean放一个容器里,逻辑更顺。

    要是你项目里真有多个完全独立的容器(比如同时用了Spring和另一个框架的容器),那得给工具类加个“容器标识”——比如用Map存不同的上下文,key是容器名称,需要的时候指定key拿对应的容器。不过这种情况真的很少见,大部分项目不会搞这么复杂。真遇到了,记住“先确认容器的父子关系,再调整Bean的归属”,基本都能解决。


    工具类是线程安全的吗?

    是的。工具类通过volatile修饰ApplicationContext变量,保证多线程下上下文对象的“可见性”(一个线程修改后,其他线程能立即看到最新值);同时用双重检查锁确保上下文的唯一性,避免并发场景下重复初始化的问题。亲测在高并发(如1000+请求/秒)场景下也能稳定工作。

    有现成的替代工具类吗?

    有。比如Spring Cloud提供的ApplicationContextHolder、Spring Boot自动配置中的org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration里的上下文工具,但本质和本文工具类原理一致。自己实现的好处是可以根据项目需求调整(比如增加“根据Bean类型批量获取”的方法),更灵活可控。

    工具类会导致内存泄漏吗?

    不会。Spring容器关闭时(如Web项目停止Tomcat、Java项目退出JVM),会自动销毁所有注册的Bean(包括工具类)。此时静态变量ApplicationContext会随着JVM的退出被回收,不存在内存泄漏的风险。

    项目中有多个Spring容器怎么办?

    通常一个项目只有一个“根容器”(如Spring Boot的AnnotationConfigApplicationContext或传统Spring的ClassPathXmlApplicationContext)。若存在父子容器(如Spring MVC的DispatcherServlet子容器和根容器),工具类会默认获取根容器——此时需注意:若Bean注册在子容器(如Controller),根容器无法直接访问,需将Bean调整到根容器扫描范围,或单独处理子容器的上下文。

    哪些场景必须用这个工具类?

    当你需要在非Spring管理的类中访问Bean时,必须用工具类。常见场景包括:①静态工具类中的业务逻辑(如本文提到的“库存扣减工具类”);②第三方任务框架的Job类(如quartz、xxl-job);③支付宝/微信支付的回调接口(非Spring创建的类);④自定义工厂类(非Spring实例化)。这些场景无法通过@Autowired注入,只能主动获取上下文。

    原文链接:https://www.mayiym.com/46802.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码