I was using my spring boot 3.2.4 app on Win 10 and Win Server 2019 and it worked fine. I know my architecture of the app is not good, but it worked. After I had to migrate to Ubuntu 24.04 , running the same app throws NullPointerException
in ApplicationContextProvider.getApplicationContext()
. JDK on all machines is the same - openjdk-18.0.1
I am using a "simple" way to get applicationContext
, which i found convenient, found here:
@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;
}
}
The class where the error occurs:
@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
}
error:
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
Also there is this Warn during tomcat starting before ERROR, basically the same:
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
I know this class is not even needed to be a Spring Bean, but i was learning injections and ApplicationContextProvider workaround worked. So before rewriting my whole code i just want to understand why the code works on win10 and not on ubuntu. I tried different jdks, nothing works. Running it with java -jar in all cases. Maybe there is an obvious cause and solution that i can't see. Tried to do steps from here - no effect: applicationContextProvider is not being called
I think the error has to do with the order of component initialization when Spring is building the context.
There is no guarantee that ApplicationContextProvider
is going to be instantiated before MainBuilderUtil
. The order in which the beans are built can change based upon many different things and can change seemingly randomly when spring does not know about a required dependency.
The easy fix for this is to use @DependsOn on MainBuilderUtil
to make sure ApplicationContextProvider
gets created first.
The cleaner fix is to recognize that 'MainChannelCredentialsUtil' is already being autowired into the constructor for MainBuilderUtil
. The constructor could initialize credentialMain
and clientMain
and not use ApplicationContextProvider
at all.
In general it is a bad idea to try to use the applicationContext to get beans during component instantiation because it breaks Spring's normal dependency resolution mechanisms and requires using hacks like @DependsOn
I would also like to note that if MainChannelCredentialsUtil
was not being auto wired into MainBuilderUtil
, then there would be no guarantee that it would get created before MainBuilderUtil
. In that case, it would not be available through ApplicationContextProvider
either.