SpringBoot使用SpringContextUtils.getBean启动报空指针处理记录

本文介绍了在旧系统中添加OSS服务时遇到的Bean初始化顺序问题。通过分析,发现需要确保SpringContextUtils在特定Bean之前初始化。通过使用Spring的@DependsOn注解解决了这个问题,确保了Bean的正确初始化顺序。此外,还详细解释了@DependsOn的作用和使用方法,包括控制Bean初始化顺序和解决循环依赖。

目录

一、现象

二、解决

三、@DependsOn详解

一、现象

        最近需要在一个比较老的系统中添加OSS进行文件上传下载处理,而原有系统中已有处理文件上传和下载处理的相关抽象类和接口,为了不影响原有代码逻辑,只能通过新增实现类的模式进行处理。原有处理逻辑中具体处理实现是通过Spring的Configuration中初始化Bean的方式进行处理(通过application的配置来决定初始化Bean),原有的处理为共享磁盘和SFTP两种方式,两种方式都没有实现的Bean里面调用Spring管理的其他Bean模式进行处理。但是OSS的处理逻辑为了兼容项目中多种模式,通过OssTemplate进行处理,并且在外部封装了对外的API,用以实现系统原有逻辑不变更和简化处理逻辑,封装的服务里面很多Bean都是Spring托管的,为了能够使用这个封装的服务,需要基于原有接口的实现类也用spring托管的Bean,故在初始化中使用了SpringContextUtils类的getBean(Class)方法获取,在WEB应用中正常启动,但是在批处理的应用中报空指针异常,沙雕了。

二、解决

        此处由于需要用Spring管理的bean,故通过查看Spring的Bean加载顺序,发现通过包的远近来进行加载,针对提供的基础服务,SpringContextUtils是否在Configuration的Bean初始化之前初始化是未知的。此时想到了很久之前有个大神处理生产问题时用到的@DependsOn注解(注解含义就是组件A要依赖于另一个组件B,也就是说被依赖的组件B会比组件A先注册到Spring容器中),如果Configuration的@Bean上面增加这个@DependsOn("springContextUtils")是否也跟正常@Component上增加注解一样,先把SpringContextUtils注册到Spring容器里面?

    @Bean
	@DependsOn({"springContextUtils"})
	public FileUploadTemplate getFileSystemMethod() {

再次启动批处理后,一切OK,不会再有空指针异常发生,并且能够正常使用了。

PS:SpringContextUtils源码如下:

@Component
public final class SpringContextUtils implements ApplicationContextAware {

    /**
     * Spring应用上下文环境
     */
    private static ApplicationContext applicationContext;

    /**
     * 获取对象
     *
     * @return Object 一个以所给名字注册的bean的实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        return (T) applicationContext.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     */
    public static <T> T getBean(Class<T> clz) {
        return applicationContext.getBean(clz);
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @return boolean
     */
    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    /**
     * @return Class 注册对象的类型
     */
    public static Class<?> getType(String name) {
        return applicationContext.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     */
    public static String[] getAliases(String name) {
        return applicationContext.getAliases(name);
    }

    @Override
    public synchronized void setApplicationContext(ApplicationContext applicationContext) {
        if (SpringContextUtils.applicationContext == null) {
            SpringContextUtils.applicationContext = applicationContext;
        }
    }
}

三、@DependsOn详解

@DependsOn注解是Spring框架中的一个功能,用于定义bean之间的依赖关系。它主要用于控制bean的初始化顺序,确保某些bean在其他bean之前被创建和初始化。@DependsOn注解常用于解决循环依赖、确保某些bean在其他bean之前被创建等场景。

具体作用:

  • 控制bean的初始化顺序:通过@DependsOn可以确保某个bean在另一个bean之前被初始化,从而避免因初始化顺序不当而导致的问题。
  • 解决循环依赖:当两个或多个bean之间存在循环依赖时,可以使用@DependsOn注解来指定bean的初始化顺序,从而解决循环依赖问题。

使用方法: @DependsOn注解可以与@Bean、@Component、@Service等注解一起使用。以下是@DependsOn的一些使用示例:

在Java配置类中,结合@Bean注解使用:

@Configuration
public class AppConfig {

    @Bean
    @DependsOn("beanB")
    public BeanA beanA() {
        return new BeanA();
    }

    @Bean
    public BeanB beanB() {
        return new BeanB();
    }
}

在上面的示例中,beanA依赖于beanB,所以我们使用@DependsOn("beanB")注解来确保beanB在beanA之前被创建和初始化。

在组件类中,结合@Component、@Service等注解使用:

@Service
@DependsOn("beanB")
public class BeanA {
    // ...
}

@Service
public class BeanB {
    // ...
}

在上面的示例中,我们将@DependsOn注解应用于BeanA类上,以确保BeanB在BeanA之前被创建和初始化。

### 使用方法 `SpringContextUtils.getBean` 方法用于从 Spring 应用上下文(`ApplicationContext`)中获取 Bean 实例。以下是使用该方法的示例: ```java import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; // 自定义 SpringContextUtils 类 class SpringContextUtils { private static ApplicationContext applicationContext; public static void setApplicationContext(ApplicationContext context) { applicationContext = context; } public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } public static Object getBean(String name) { return applicationContext.getBean(name); } } // 示例 Bean 类 class MyService { public void doSomething() { System.out.println("Doing something..."); } } public class Main { public static void main(String[] args) { // 加载 Spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 设置应用上下文到工具类 SpringContextUtils.setApplicationContext(context); // 通过类型获取 Bean MyService myService = SpringContextUtils.getBean(MyService.class); myService.doSomething(); // 通过名称获取 Bean MyService myServiceByName = (MyService) SpringContextUtils.getBean("myService"); myServiceByName.doSomething(); } } ``` ### 原理 `SpringContextUtils.getBean` 方法的核心原理依赖于 Spring 的 `ApplicationContext`。`ApplicationContext` 是 Spring 框架中用于管理 Bean 的核心接口,它负责加载 Bean 的定义、创建和管理 Bean 实例。 当调用 `SpringContextUtils.getBean` 方法时,实际上是委托给 `ApplicationContext` 的 `getBean` 方法来完成 Bean 的查找和获取。`ApplicationContext` 内部维护了一个 Bean 定义注册表和 Bean 实例缓存。在启动时,`ApplicationContext` 会解析配置文件(如 XML 配置文件或 Java 注解),将 Bean 的定义信息存储在注册表中。当调用 `getBean` 方法时,`ApplicationContext` 会根据传入的 Bean 名称或类型,在注册表中查找对应的 Bean 定义,并根据定义创建或获取 Bean 实例。 如果 Bean 是单例模式,`ApplicationContext` 会在第一次创建 Bean 实例后将其缓存起来,后续调用 `getBean` 方法时直接从缓存中获取。如果 Bean 是原型模式,每次调用 `getBean` 方法时都会创建一个新的 Bean 实例。 ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值