Java 在 class 初始化后调用方法
Java call method after class is initialized
我有一个 class X,它由各种 classes 扩展。 X 需要知道它在运行时存在的每个子类型。所以我的想法是在 X 中创建一个以 Class 作为参数的静态方法 "static init()"。问题是 X 的 subclass 需要在调用 init() 之前通过其静态初始化块进行初始化。例如,X 可能会发现 subclass 的某些静态字段,这些字段可能是任何已声明的类型。所以以这段代码为例:
class X {
X() {
/*This would be one idea but does not work
in this concrete example.*/
init(getClass());
}
static void init(Class<? extends X> c) {
if(c.getDeclaredField("a").get(null) == null) {
throw new AssertionError();
}
}
}
class Y extends X {
static X a = new X();
static {
/*Another idea. Works totally fine but
I dont want to trust subclasses that
they do that correctly.*/
init(Y.class);
}
}
所以我正在寻找一种方法,以某种方式让我的 init() 方法被称为 class 静态初始化的最后一步。或任何其他防止 AssertionError 发生的方法。
编辑:因为这导致评论中的误解,我其实想做第二件事。我希望在 subclass 是我的 class 的任何 class 的静态初始化中调用我的方法(直接或间接)。我不知道存在哪些 subclasses,我对它们的结构一无所知,所有断言都必须在运行时检查(通过反射)。我的 class 和子 class 甚至可能不是同一个人在同一台机器上由同一编译器编译的 and/or。
Edit2:也许我可以构建一个 ClassLoader,它是 System-ClassLoader 的简单代理,并用它替换 System-ClassLoader。如果加载的 class 是我的 class 的子 class,我可以初始化它并调用我的方法。不幸的是,我对 ClassLoaders 知之甚少(目前)并且网上没有太多关于此主题的教程或帮助。那么任何有自定义 ClassLoaders 经验的人都可以告诉我这是否可能?如果我的 class 路径中的某些其他库在任何时间点也安装了自定义 ClassLoader 会怎样?
Edit3:我不想做的 - 如果有任何其他选择 - 是直接修改字节码。
X needs to know each subtype that exists of it at runtime.
这可以通过以下代码实现:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static class X {
private static Set<Class<? extends X>> classes = new HashSet<Class<? extends X>>();
public X() {
classes.add(getClass());
// and here you can check for fields of subclasses
}
}
public static class Y extends X {}
public static void main(String[] args) {
new Y(); new X();
System.out.println(X.classes);
}
}
如果你 运行 main 方法,它会打印如下内容:
[class Main$X, class Main$Y]
如果你必须处理很多反射的东西我可以推荐reflections libary
你可以用来做什么:
Reflections r = new Reflections("package.name");
Set<Class<? extends X>> childs = r.getSubTypesOf(X.class);
这将是您的 Edit2 的示例:
public class ConstraintClassLoader extends URLClassLoader {
public ConstraintClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
System.out.println("find " + name);
Class<?> c = super.findClass(name);
Class<?> parent = c.getSuperclass();
while (parent != null) {
if (parent.getName().contains("X")) {
break;
}
parent = parent.getSuperclass();
}
if (parent == null) {
return c;
}
Field declaredField = c.getDeclaredField("a");
declaredField.setAccessible(true);
if (declaredField.get(null) == null) {
throw new AssertionError();
}
return c;
} catch (NullPointerException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException | AssertionError e) {
throw new RuntimeException(e);
}
}
}
我认为它可以实现您在 Edit2 中描述的功能,但它也存在您提到的弱点(与其他 class 加载应用程序(如 OSGI、Reflections、AOP、Spring 一起使用时会很脆弱) .
要安装此 class 加载程序,您可以使用此 class 加载程序加载 Main class 并使用反射调用其上的主要方法。您可以在网络上找到其他更优雅的设置 classloader 的解决方案。
我有一个 class X,它由各种 classes 扩展。 X 需要知道它在运行时存在的每个子类型。所以我的想法是在 X 中创建一个以 Class 作为参数的静态方法 "static init()"。问题是 X 的 subclass 需要在调用 init() 之前通过其静态初始化块进行初始化。例如,X 可能会发现 subclass 的某些静态字段,这些字段可能是任何已声明的类型。所以以这段代码为例:
class X {
X() {
/*This would be one idea but does not work
in this concrete example.*/
init(getClass());
}
static void init(Class<? extends X> c) {
if(c.getDeclaredField("a").get(null) == null) {
throw new AssertionError();
}
}
}
class Y extends X {
static X a = new X();
static {
/*Another idea. Works totally fine but
I dont want to trust subclasses that
they do that correctly.*/
init(Y.class);
}
}
所以我正在寻找一种方法,以某种方式让我的 init() 方法被称为 class 静态初始化的最后一步。或任何其他防止 AssertionError 发生的方法。
编辑:因为这导致评论中的误解,我其实想做第二件事。我希望在 subclass 是我的 class 的任何 class 的静态初始化中调用我的方法(直接或间接)。我不知道存在哪些 subclasses,我对它们的结构一无所知,所有断言都必须在运行时检查(通过反射)。我的 class 和子 class 甚至可能不是同一个人在同一台机器上由同一编译器编译的 and/or。
Edit2:也许我可以构建一个 ClassLoader,它是 System-ClassLoader 的简单代理,并用它替换 System-ClassLoader。如果加载的 class 是我的 class 的子 class,我可以初始化它并调用我的方法。不幸的是,我对 ClassLoaders 知之甚少(目前)并且网上没有太多关于此主题的教程或帮助。那么任何有自定义 ClassLoaders 经验的人都可以告诉我这是否可能?如果我的 class 路径中的某些其他库在任何时间点也安装了自定义 ClassLoader 会怎样?
Edit3:我不想做的 - 如果有任何其他选择 - 是直接修改字节码。
X needs to know each subtype that exists of it at runtime.
这可以通过以下代码实现:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static class X {
private static Set<Class<? extends X>> classes = new HashSet<Class<? extends X>>();
public X() {
classes.add(getClass());
// and here you can check for fields of subclasses
}
}
public static class Y extends X {}
public static void main(String[] args) {
new Y(); new X();
System.out.println(X.classes);
}
}
如果你 运行 main 方法,它会打印如下内容:
[class Main$X, class Main$Y]
如果你必须处理很多反射的东西我可以推荐reflections libary
你可以用来做什么:
Reflections r = new Reflections("package.name");
Set<Class<? extends X>> childs = r.getSubTypesOf(X.class);
这将是您的 Edit2 的示例:
public class ConstraintClassLoader extends URLClassLoader {
public ConstraintClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
System.out.println("find " + name);
Class<?> c = super.findClass(name);
Class<?> parent = c.getSuperclass();
while (parent != null) {
if (parent.getName().contains("X")) {
break;
}
parent = parent.getSuperclass();
}
if (parent == null) {
return c;
}
Field declaredField = c.getDeclaredField("a");
declaredField.setAccessible(true);
if (declaredField.get(null) == null) {
throw new AssertionError();
}
return c;
} catch (NullPointerException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException | AssertionError e) {
throw new RuntimeException(e);
}
}
}
我认为它可以实现您在 Edit2 中描述的功能,但它也存在您提到的弱点(与其他 class 加载应用程序(如 OSGI、Reflections、AOP、Spring 一起使用时会很脆弱) .
要安装此 class 加载程序,您可以使用此 class 加载程序加载 Main class 并使用反射调用其上的主要方法。您可以在网络上找到其他更优雅的设置 classloader 的解决方案。