我正在使用jdk1.8.0_221
,我想根据调用两种不同类型的方法在超类的构造函数中加载特定配置。我正在寻找最佳实践,最好是一种方便的方法。
由于简化,我将代码情况模拟为以下代码片段:
package test;
public class A extends Z{
@Test
@Marker1
public void f1() {
Z.g();
}
@Test
@Marker2
public void f2() {
Z.g();
}
}
package test;
public class B extends Z{
@Test
@Marker1
public void f3() {
Z.g();
}
@Test
@Marker2
public void f4() {
Z.g();
}
}
package core;
public class Z{
public Z() {
//I want to determine here that which type of method calls this constructor
//The caller could be Marker1 or Marker2
//And based on the caller, load the corresponding configuration
}
public static void g(){
//some code goes here
}
}
注意1:有很多来自不同类的方法调用Z.g()
,所以我不能使用类的显式名称来获取方法及其注释。
注意2:所有配置都应在Z超类的构造函数中完成。
注3:g()
方法不一定是静态的。
我尝试了以下代码片段,但getMethodName()
返回总是<init>
:
public Z() throws NoSuchMethodException, ClassNotFoundException{
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
StackTraceElement ste = stElements[3];// or stElements[2]
Class<?> aClass = Class.forName(ste.getClassName());
String methodName = ste.getMethodName();
Method method = aClass.getMethod(methodName);
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.getClass().equals(Marker1.class)) {
//load conf1
break;
} else if (annotation.getClass().equals(Marker2.class)) {
//load conf2
break;
}
}
}
此外,我在堆栈溢出和其他社区中尝试了许多无法正常工作的解决方案。
你得到的结果,准确地告诉你发生了什么。您的两个方法都没有调用构造函数。
所有方法都有以下形式
@Test
@MarkerN
public void fX() {
Z.g();
}
因此,它们包含对类Z
的static
方法g()
的调用。但是没有实例化Z
.
相反,您的类A
和B
是Z
的子类。由于您没有为它们声明显式构造函数,因此编译器会为它们生成默认构造函数,例如
public class A extends Z {
public A() {
super(); // here, the constructor of Z gets called
}
// the other methods …
}
因此,反射查找会告诉你Z
构造函数的调用发生在一个名为<init>
的方法中,该方法是A
的构造函数,或者。B
的构造函数B
。
如果方法确实new Z().g()
,那就不一样了,但这会使依赖在static
方法调用之前执行的构造函数的可疑设计变得更糟。
这个问题应该在框架端解决,框架端同时做,实例化A
和B
,并调用f1()
方法来f4()
它们。 例如,使用 JUnit 5,解决方案如下所示:
package test;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import org.junit.jupiter.api.*;
public class A {
@BeforeEach
public void prepare(TestInfo ti) {
Z.loadConfiguration(ti.getTestMethod().get());
}
@Test
@Marker1
public void f1() {
Z.g();
}
@Test
@Marker2
public void f2() {
Z.g();
}
}
class Z {
public static void loadConfiguration(AnnotatedElement e) {
if(e.isAnnotationPresent(Marker1.class)) {
System.out.println("Marker1");
}
else if(e.isAnnotationPresent(Marker2.class)) {
System.out.println("Marker2");
}
}
public static void g() {
System.out.println("Z.g()");
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface Marker1 {}
@Retention(RetentionPolicy.RUNTIME)
@interface Marker2 {}
Marker1
Z.g()
Marker2
Z.g()