在Spring Boot应用程序启动期间初始化静态变量后,将取消设置静态变量



我正试图从Java服务提供商(META-INF/services(的静态代码中访问Spring Boot ApplicationContext中实例化的bean。我使用的是Java 14(openjdk:14-jdk-slim(。

在远程调试器会话中,我可以看到应用程序上下文CONTEXT的静态变量被设置为正确的值:

@Component
@Slf4j
public class ApplicationContextListener implements ApplicationContextAware {
private static volatile ApplicationContext CONTEXT;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CONTEXT = applicationContext;
}
...

下次在运行时(从另一个线程(访问它时,变量CONTEXT为null,例如:

public static MyBean getMyBean() {
return CONTEXT.getBean(MyBean.class);
}

我怀疑问题是持有静态变量的类得到了";卸载";介于两者之间。但是1(为什么在应用程序上下文中定义有效bean的类会发生这种情况2(我如何避免这种情况?

在应用程序执行过程中,静态变量稍后变为null的原因是什么?除了上面列出的代码外,没有任何代码设置变量。setApplicationContext只被调用一次。

如果问题与类卸载有关,我如何避免这种情况?

也许我的初始化代码是针对类加载器a加载的类中的静态变量执行的,并且正在访问类加载器B加载的同一类中未初始化的静态变量?假设这就是原因,我该如何解决这个问题?

谢谢!

更新

当在方法setApplicationContext中时,类ApplicationContextListener实例的类加载器是this.getClass().getClassLoader()返回的org.springframework.boot.devtools.restart.classloader.RestartClassLoader

当调试器稍后在ApplicationContextListener类静态方法中停止时,调试器显示ApplicationContextListener.class.getClassLoader()ClassLoaders$AppClassLoader

后一种情况下的Thread.currentThread().getContextClassLoader()(在静态get中(也显示了org.springframework.boot.devtools.restart.classloader.RestartClassLoader

原因是Spring Boot Automatic Restart功能在默认情况下通过依赖于我用于创建项目的Spring Initializer添加的spring-boot-devtools而启用。

该特性允许通过附加的类加载器加载bean类。

在这种情况下,访问静态变量的一种方法如下:

private static ApplicationContext getContext() {
try {
return (ApplicationContext) getClass(ApplicationListenerContext.class.getName())
.getField("CONTEXT").get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null) {
classLoader = ApplicationListenerContext.class.getClassLoader();
}
return classLoader.loadClass(classname);
}

另一方面,这种变通方法并不是真正需要的。通过将-Dspring.devtools.restart.enabled=false传递给应用程序启动参数,可以关闭自动重新启动功能。

更简单的解决方案是从我打包到服务Docker镜像的分解库中排除spring-boot-devtools依赖项:

<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeArtifactIds>spring-boot-devtools</excludeArtifactIds>
</configuration>
</execution>
</executions>
</plugin>

以下SO问题提供了有关此主题的更多详细信息:

类加载器错误-加载器org.springframework.boot.devtools.restart.classloader.RestartClassLoader 的未命名模块

什么是NoSuchBeanDefinitionException?我该如何修复它?

最新更新