我们有一个 WAR 应用程序,它使用第三方库创建一个线程池,其中一个正在运行的线程作为其内部类之一的静态字段。当我们取消部署 WAR 时,该线程继续运行。为了解决这个问题,我想在取消部署期间运行一些代码,它使用反射来访问私有静态字段并关闭线程池。
线程池是在类的静态初始化程序中创建的。当我们的关闭代码运行时,有问题的类可能尚未加载或初始化,并且我不希望关闭代码初始化该类,只是为了再次关闭它(如果我可以避免这种情况)。
我可以使用
Class.forName()
的三参数形式来获取类而无需初始化它。然而,得到我想要的Field
后,获取字段的值仍然会触发类被初始化。我正在寻找一种方法来避免这种情况。
这是一些示例代码:
package scratch;
public class A {
private static final String field;
static {
System.out.println("A initializing");
field = "I'm initialized!";
}
}
package scratch;
public class B {
public static void main(String[] args) {
// var a = new A();
try {
System.out.println("B starting");
var clazz = Class.forName("scratch.A", false, B.class.getClassLoader());
System.out.println("B got class");
var field = clazz.getDeclaredField("field");
System.out.println("B got field");
field.setAccessible(true);
System.out.println("B set accessible");
var value = field.get(null);
System.out.println("B got value '" + value + "'");
} catch (Exception e) {
System.out.println(e);
}
}
}
运行此代码会产生以下输出:
B starting
B got class
B got field
B set accessible
A initializing
B got value 'I'm initialized!'
对
Field.get()
的调用会触发类的初始化。
我只是想避免在访问该字段的过程中初始化该类(如果尚未初始化)以将其关闭。如果尚未加载类,是否有任何方法可以避免获取该类?或者在获取字段之前测试它是否已初始化?或者在不触发初始化的情况下获取字段的值?
静态块代码在类加载期间仅执行一次。 此时:
static {
System.out.println("A initializing");
field = "I'm initialized!";
}
类没有初始化,只是加载到JVM中。如果未加载类,则无法通过反射获取类的信息。
解决此问题的唯一方法是不要将初始化与类加载联系起来。没关系,因为无论如何这都是个坏主意。所以重写你的类:
public class A {
private static A LATEST_INSTANCE;
private String field;
private A() {
System.out.println("A initializing");
field = "I'm initialized!";
LATEST_INSTANCE = this;
}
}
现在您可以访问
LATEST_INSTANCE
字段并对其状态执行任何您想要的操作。你甚至不需要反思!无论您需要什么,您都可以创建静态方法来访问和访问或初始化实例。