动态获取Bean实例为null的问题

应用场景

今天在开发的时候遇到一个问题:因为公司用的是低代码开放平台,写业务逻辑的时候只能是调用static方法,但是我之前写了个业务逻辑是非静态的。现在要加一个手动的按钮,调用的逻辑和之前的逻辑一模一样,我就想着直接在静态方法里面获取bean的实例,然后直接调用之前写好的方法就可以了。在这个过程中遇到了点小问题,这里记录一下。

获取Bean实例

获取bean实例的方法有很多种,这里我主要说我用到的通过ApplicationContext 获取Bean实例。因为公司在启动类封装了一层,所以后面我也没用这个,但是我还是要记录一下。
注意:如果启动类封装了一层,已经把ApplicationContext 初始化了,可以省略这一步,直接用启动类封装好的就行。

package cloud.app.oms.setting.utils;
import cloud.logproxy.LogProxy;
import lombok.Getter;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

@Component("SpringContextUtil")
public class SpringContextUtil implements ApplicationContextAware {
    @Getter
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }
    
	public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    public static <T> T getBean(Class<T> beanClass) {
        if (applicationContext == null) {
            LogProxy.Error("获取"+beanClass.getName()+"的bean异常!");
            return null;
        };
        return applicationContext.getBean(beanClass);
    }
}

使用:

// 通过类型获取 Bean
MyService myService = SpringContextUtil.getBean(MyService.class);

// 通过名称获取 Bean
MyService myService = (MyService) SpringContextUtil.getBean("myService");

也可以通过 SpringApplication.run 返回的 ApplicationContext 获取 Bean

在获取Bean的过程中,我发现容器里面的类加载器,和代码里面的类加载器不一致,导致我获取Bean的时候一直获取不到。

可以打印下系统有哪些Bean,还有想要获取Bean的类加载器:

public static AResult handle() {
        System.out.println("打印所有的Bean:");
        ApplicationContext context = BootHelper.Context;
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(beanName);
        }
        Object bean = context.getBean("MessageSender");
        System.out.println("容器中的类加载器:");
        System.out.println(bean.getClass().getClassLoader());
        System.out.println("代码中的类加载器:");
        System.out.println(MessageSender.class.getClassLoader());
        System.out.println("------------------------------------------------------");
        return new AResult();
    }

如果容器中的类加载器和代码中的类加载器不一致,可以 在获取 Spring 容器中的 MessageSender Bean 时,临时切换线程的上下文类加载器(Context ClassLoader),并在操作完成后恢复原始的类加载器:

public static AResult handle() {
        ApplicationContext context = BootHelper.Context;
        MessageSender messageSender;
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(MessageSender.class.getClassLoader());
            messageSender = context.getBean(MessageSender.class);
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
        return new AResult();
    }

拿到Bean后就可以调用里面的方法进行相应的业务逻辑操作了。遇到的问题主要是类加载器不一致,哪怕JVM中是同一个类名并且地址也一样,但是如果类加载器有多个,那获取Bean的时候就会出问题,这里就简单记录一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值