Springboot中SpringUtil的applicationContext一直是null

文章讲述了在SpringBoot应用中,`LoginContextHolder`中的`applicationContext`始终为null的问题,分析了SpringUtil组件未被正确扫描和注入的原因,以及临时解决方案:自定义SpringUtil并成功注入。作者对Hutool的SpringUtil为何未被识别感到困惑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

applicationContext一直是null

以下代码一直抛null,然后仔细一查是applicationContext为null.

package com.example.cw_app_back.modular.context.login;



import cn.hutool.extra.spring.SpringUtil;
import com.example.cw_app_back.modular.context.login.service.LoginContext;
import com.example.cw_app_back.modular.context.login.service.impl.LoginContextImpl;
import com.fasterxml.jackson.databind.util.BeanUtil;

/**
 * 当前登录用户信息获取的接口
 *
 */
public class LoginContextHolder {
    public static LoginContext me() {

      return   SpringUtil.getBean(LoginContextImpl.class);
    }

}

SpringUtil 代码如下

package cn.hutool.extra.spring;

import cn.hutool.core.util.ArrayUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * Spring(Spring boot)工具封装,包括:
 *
 * <pre>
 *     1、Spring IOC容器中的bean对象获取
 * </pre>
 *
 * @author loolly
 * @since 5.1.0
 */
@Component
public class SpringUtil implements ApplicationContextAware {

	private static ApplicationContext applicationContext;

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

	/**
	 * 获取applicationContext
	 *
	 * @return ApplicationContext
	 */
	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	//通过name获取 Bean.

	/**
	 * 通过name获取 Bean
	 *
	 * @param <T> Bean类型
	 * @param name Bean名称
	 * @return Bean
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 通过class获取Bean
	 *
	 * @param <T> Bean类型
	 * @param clazz Bean类
	 * @return Bean对象
	 */
	public static <T> T getBean(Class<T> clazz) {
		return applicationContext.getBean(clazz);
	}

	/**
	 * 通过name,以及Clazz返回指定的Bean
	 *
	 * @param <T> bean类型
	 * @param name Bean名称
	 * @param clazz bean类型
	 * @return Bean对象
	 */
	public static <T> T getBean(String name, Class<T> clazz) {
		return applicationContext.getBean(name, clazz);
	}

	/**
	 * 获取指定类型对应的所有Bean,包括子类
	 *
	 * @param <T> Bean类型
	 * @param type 类、接口,null表示获取所有bean
	 * @return 类型对应的bean,key是bean注册的name,value是Bean
	 * @since 5.3.3
	 */
	public static <T> Map<String, T> getBeansOfType(Class<T> type){
		return applicationContext.getBeansOfType(type);
	}

	/**
	 * 获取指定类型对应的Bean名称,包括子类
	 * @param type 类、接口,null表示获取所有bean名称
	 * @return bean名称
	 * @since 5.3.3
	 */
	public static String[] getBeanNamesForType(Class<?> type){
		return applicationContext.getBeanNamesForType(type);
	}

	/**
	 * 获取配置文件配置项的值
	 *
	 * @param key 配置项key
	 * @return 属性值
	 * @since 5.3.3
	 */
	public static String getProperty(String key) {
		return applicationContext.getEnvironment().getProperty(key);
	}

	/**
	 * 获取当前的环境配置,无配置返回null
	 *
	 * @return 当前的环境配置
	 * @since 5.3.3
	 */
	public static String[] getActiveProfiles(){
		return applicationContext.getEnvironment().getActiveProfiles();
	}

	/**
	 * 获取当前的环境配置,当有多个环境配置时,只获取第一个
	 *
	 * @return 当前的环境配置
	 * @since 5.3.3
	 */
	public static String getActiveProfile(){
		final String[] activeProfiles = getActiveProfiles();
		return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
	}
}





启动类:

package com.example.cw_app_back;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

import javax.annotation.PostConstruct;
import java.util.TimeZone;

@SpringBootApplication
@MapperScan(basePackages = "com.example.cw_app_back.**.mapper")
@ServletComponentScan
public class CwAppBackApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(CwAppBackApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(this.getClass());
    }

}

但是debug的时候发现这个SpringUtil一直都没进去过.不知道为什么这个Component一直没扫到,注入不进去.但是看看启动类好像没啥毛病啊.

解决:

自己又在自己的包下面写了一遍一模一样的SpringUtil ,然后调用自己的SpringUtil,自己的SpringUtil会注入成功的,也算是解决了,但是很迷茫为什么读不到hutool的SpringUtil,为什么不认.

### 关于 `SpringUtil` 的错误信息及解决方案 在处理 Spring 框架中的工具类问题时,特别是像 `SpringUtil` 这样可能自定义或第三方提供的工具类,遇到错误通常涉及依赖注入失败、上下文未正确加载等问题。下面是一些常见的错误及其对应的解决方案。 #### 1. 上下文获取异常 当尝试通过静态方法访问应用程序上下文(ApplicationContext),而此时上下文尚未初始化完成,则会抛出 `NullPointerException` 或其他类似的异常。为了避免这种情况发生,在创建 `SpringUtil` 类时应确保其能够安全地延迟加载 ApplicationContext 实例[^1]: ```java public class SpringUtil { private static ApplicationContext applicationContext; public static void setApplicationContext(ApplicationContext context) throws BeansException { if (applicationContext == null) { applicationContext = context; } } @SuppressWarnings("unchecked") public static <T> T getBean(String name) { return (T) applicationContext.getBean(name); } } ``` 此实现允许开发者在 Web 应用启动期间设置实际的应用程序上下文实例,并提供了一个通用的方法来按名称检索 Bean 对象。 #### 2. 非Web环境下的使用不当 对于非Web项目来说,默认情况下不会自动调用 `setApplicationContext()` 方法去注册 ApplicationListener 来监听 ContextRefreshedEvent 事件并赋值给静态变量。因此需要手动配置以支持此类场景下的正常工作。 可以在主函数中加入如下代码片段作为临时措施: ```java @SpringBootApplication public class MyApplication implements CommandLineRunner { @Autowired private ConfigurableApplicationContext configurableApplicationContext; public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } @Override public void run(String... arg0) throws Exception { SpringUtil.setApplicationContext(configurableApplicationContext); } } ``` #### 3. 版本兼容性问题 随着不同版本之间的迭代更新,某些 API 可能会发生变化甚至被废弃掉。所以在引入新的库文件之前一定要仔细阅读官方文档说明以及迁移指南,确认所使用的功能是否仍然有效并且保持向后兼容性。 例如,从 Spring Framework 5 开始,部分旧版接口已被移除;如果继续沿用可能会导致编译报错或是运行期不稳定的情况出现。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值