我正试图从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?我该如何修复它?