我在 Win 10 和 Win Server 2019 上使用我的 spring boot 3.2.4 应用程序,它运行良好。我知道我的应用程序架构不好,但它有效。在我不得不迁移到 Ubuntu 24.04 后,运行相同的应用程序会在
NullPointerException
中抛出 ApplicationContextProvider.getApplicationContext()
。所有机器上的 JDK 都是相同的 - openjdk-18.0.1
我正在使用一种“简单”的方式来获取
applicationContext
,我发现这很方便,可以在这里找到:
@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 未被调用
我认为你的错误与 Spring 构建上下文时组件创建的顺序有关。
不保证
ApplicationContextProvider
会在MainBuilderUtil
之前被实例化。构建 bean 的顺序可以根据许多不同的事情而改变,并且当 spring 不知道所需的依赖项时,可以看似随机地改变。
解决此问题的简单方法是在 MainBuilderUtil
上使用
@DependsOn以确保首先创建
ApplicationContextProvider
。
更干净的修复是认识到“MainChannelCredentialsUtil”已经被自动装配到
MainBuilderUtil
的构造函数中。您可以只在 credentialMain
的构造函数中初始化 ClientMain
和 MainBuilderUtil
而根本不使用 ApplicationContextProvider
。
一般来说,尝试使用 applicationContext 来获取在组件实例化期间使用的 bean 是一个坏主意,因为它破坏了 Spring 正常的依赖解析机制,并且需要使用像 @DependsOn 这样的 hacks