当前位置: 首页 > 知识库问答 >
问题:

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

夔光霁
2023-03-14

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

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

@Component
@Slf4j
public class ApplicationContextListener implements ApplicationContextAware {
    private static volatile ApplicationContext CONTEXT;

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

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

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

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

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

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

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

非常感谢。

使现代化

当在方法setApplicationContext中时,类ApplicationContextListener实例的类加载器是org。springframework。靴子开发工具。重新启动。类加载器。重新启动ClassLoader,此操作返回。getClass()。getClassLoader()。

当调试器稍后在ApplicationContextListenerclass静态方法中停止时,调试器将为ApplicationContextListener显示ClassLoaders$AppClassLoader。班getClassLoader()

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

共有1个答案

罗毅
2023-03-14

原因是Spring Boot自动重启功能在默认情况下通过依赖于我用来创建项目的Spring初始值设定项添加的Spring Bootdevtools而启用。

该特性允许通过额外的类加载器加载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可以关闭自动重启功能。开发工具。重新启动。应用程序启动参数的enabled=false

更简单的解决方案是从我打包到服务Docker映像中的分解库中排除sping-boo-devols依赖关系:

        <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>

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

类加载器错误-加载器组织的未命名模块。springframework。靴子开发工具。重新启动。类加载器。重新启动ClassLoader

什么是NoSuchBean定义异常以及如何修复它?

 类似资料:
  • 问题内容: 我想知道为什么默认情况下C,C ++和Java中的确切静态变量初始化为零?为什么对局部变量不是这样? 问题答案: 为什么要对静态变量进行确定性初始化而对局部变量不进行初始化? 了解如何实现静态变量。 它们的内存在链接时分配,并且它们的初始值也在链接时提供。 没有运行时开销。 另一方面,用于局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前在那里。如果需要,可以清除该内存(将其清

  • 问题内容: 如何在Java中初始化类的私有静态成员。 尝试以下操作: 但是在创建类A的第二个对象然后调用f1()时,我得到了空指针异常。 问题答案: 初始化静态成员的首选方法是(如上所述) 或者对于更复杂的初始化代码,您可以使用静态初始化程序块:

  • 我正在开发我的第一个 Swing 应用程序,现在提出了一个难题:在静态初始化期间或开始实际执行后执行引导和资源初始化。我是什么意思...我有单例: 因此,方法如下所示 或者,也许我在启动后手动初始化资源,然后运行它。逻辑上正确的方式是什么?

  • 问题内容: 当我运行此代码时,答案是1,我想应该是2。初始化的顺序和每一步中k的值是什么? 编辑1:作为后续的“ k设置为默认值”,那么为什么下一个代码不能编译?出现错误“在定义字段之前无法引用它”。 编辑2:出于某种我不知道的原因,它^可以在其“ Test.k”代替“ k”时使用。 感谢所有的答案。这将满足:D 问题答案: 它们按照您编写它们的顺序执行。如果代码是: 然后输出变为2。 初始化的顺

  • 问题内容: 我有以下代码: 这给了我以下错误: 解析错误:语法错误,在第19行的/home/user/Sites/site/registration/inc/registration.class.inc中出现意外的’(’,期待’)’ 所以,我想我做错了什么…但是如果不那样做怎么办?如果我用常规字符串更改mktime内容,它将起作用。所以,我知道我能做到这一点 的那种 像.. 有人有指针吗? 问题答

  • 问题内容: 我想知道静态变量何时初始化为其默认值。加载类时,先创建(分配)静态变量,然后执行静态初始化程序和声明中的初始化是否正确?在什么时候给出默认值?这导致前向参考的问题。 另外,如果你可以参考“ 为什么没有及时初始化静态字段?”这一问题进行解释,尤其是Kevin Brock在同一网站上给出的答案。我不明白第三点。 问题答案: 从请参阅Java静态变量方法: 它是一个属于类而不属于对象(实例)