为什么 ApplicationContextProvider 在另一个系统上抛出 NullPointerException

问题描述 投票:0回答:1

我在 Win 10 和 Win Server 2019 上使用我的 spring boot 3.2.4 应用程序,它运行良好。我知道我的应用程序架构不好,但它有效。在我不得不迁移到 Ubuntu 24.04 后,运行相同的应用程序会在

NullPointerException
中抛出
ApplicationContextProvider.getApplicationContext()
。所有机器上的 JDK 都是相同的 - openjdk-18.0.1

我正在使用一种“简单”的方式来获取

applicationContext
,我发现这很方便,可以在这里找到:

ApplicationContextProvider

@Component
public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        ApplicationContextProvider.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}

发生错误的类:

@Component
public class MainBuilderUtil {
    MainChannelCredentialsUtil mainChannelCredentialsUtil;

    public final OAuth2Credential credentialMain = new OAuth2Credential("provider",
            ApplicationContextProvider.getApplicationContext().getBean(MainChannelCredentialsUtil.class).getMainToken());

    @Autowired
    private MainBuilderUtil(MainChannelCredentialsUtil mainChannelCredentialsUtil) {
        this.mainChannelCredentialsUtil = mainChannelCredentialsUtil;
    }

    public final Client ClientMain =
            ClientBuilder.builder()
                    .withEnableChat(true)
                    .withChatAccount(credentialMain)
                    .build();

    //getters
}
    

错误:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.bot.springbootbot.connections.channels.builder_utils.MainBuilderUtil]: Constructor threw exception
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:221) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:111) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:315) ~[spring-beans-6.1.5.jar!/:6.1.5]
    ... 24 common frames omitted
Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.context.ApplicationContext.getBean(java.lang.Class)" because the return value of "com.bot.springbootbot.ApplicationContextProvider.getApplicationContext()" is null
    at com.bot.springbootbot.connections.channels.builder_utils.MainBuilderUtil.<init>(MainBuilderUtil.java:18) ~[!/:0.0.1-SNAPSHOT]
    at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:208) ~[spring-beans-6.1.5.jar!/:6.1.5]
    ... 26 common frames omitted

在ERROR之前tomcat启动过程中还有这个Warn,基本一样:

WARN 6417 --- [SpringBootBot] [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainBuilderUtil' defined in URL [jar:nested:/usr/SpringBootBot/target/SpringBootBot-0.0.1-SNAPSHOT.jar/!BOOT-INF/classes/!/com/bot/springbootbot/connections/channels/builder_utils/MainBuilderUtil.class]: Failed to instantiate [com.bot.springbootbot.connections.channels.builder_utils.MainBuilderUtil]: Constructor threw exception

我知道这个类甚至不需要成为 Spring Bean,但我正在学习注入,并且 ApplicationContextProvider 解决方法有效。所以在重写我的整个代码之前我只想了解为什么代码在 win10 上运行而不是在 ubuntu 上运行。我尝试了不同的jdk,没有任何效果。在所有情况下都使用 java -jar 运行它。也许有一个我看不到的明显原因和解决方案。 尝试从这里执行步骤 - 没有效果: applicationContextProvider 未被调用

java spring-boot nullpointerexception applicationcontext
1个回答
0
投票

我认为你的错误与 Spring 构建上下文时组件创建的顺序有关。

不保证

ApplicationContextProvider
会在
MainBuilderUtil
之前被实例化。构建 bean 的顺序可以根据许多不同的事情而改变,并且当 spring 不知道所需的依赖项时,可以看似随机地改变。

解决此问题的简单方法是在 MainBuilderUtil 上使用

@DependsOn
以确保首先创建
ApplicationContextProvider

更干净的修复是认识到“MainChannelCredentialsUtil”已经被自动装配到

MainBuilderUtil
的构造函数中。您可以只在
credentialMain
的构造函数中初始化
ClientMain
MainBuilderUtil
而根本不使用
ApplicationContextProvider

一般来说,尝试使用 applicationContext 来获取在组件实例化期间使用的 bean 是一个坏主意,因为它破坏了 Spring 正常的依赖解析机制,并且需要使用像 @DependsOn 这样的 hacks

© www.soinside.com 2019 - 2024. All rights reserved.