Java 如何从另一个类获取调用方方法的注释

Java 如何从另一个类获取调用方方法的注释,java,reflection,java-8,annotations,stack-trace,Java,Reflection,Java 8,Annotations,Stack Trace,我使用的是jdk1.8.0_221,我希望在调用两种不同类型的方法的基础上,在超类的构造函数中加载特定的配置。我正在寻求最佳实践,最好是方便的方法 由于简化,我将代码情况模拟为以下代码片段: package test; public class A extends Z{ @Test @Marker1 public void f1() { Z.g(); } @Test @Marker2 public void f2() {

我使用的是
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:方法不一定是静态的

我尝试了以下代码段,但
getMethodName()
始终返回

public Z()抛出NoSuchMethodException、ClassNotFoundException{
StackTraceElement[]stElements=Thread.currentThread().getStackTrace();
StackTraceElement ste=stElements[3];//或stElements[2]
类aClass=Class.forName(ste.getClassName());
字符串methodName=ste.getMethodName();
Method=aClass.getMethod(methodName);
Annotation[]annotations=aClass.getAnnotations();
用于(注释:注释){
if(annotation.getClass().equals(Marker1.class)){
//负载会话1
打破
}else if(annotation.getClass().equals(Marker2.class)){
//负荷系数2
打破
}
}
}

此外,我在stackoverflow和其他社区尝试了许多无法正常工作的解决方案。

您得到的结果正是告诉您发生了什么。两个方法都没有调用构造函数

您的所有方法都具有以下形式:

@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
的构造函数发生在名为
的方法中,该方法是
的构造函数,分别是
B
B
的构造函数

如果方法确实执行了
newz().g()
,则情况会有所不同,但这会使依赖于在
静态方法调用之前执行的构造函数的可疑设计变得更糟

这个问题应该在框架方面解决,这两方面都在做,实例化
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()

您得到的结果准确地告诉您发生了什么。两个方法都没有调用构造函数

您的所有方法都具有以下形式:

@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
的构造函数发生在名为
的方法中,该方法是
的构造函数,分别是
B
B
的构造函数

如果方法确实执行了
newz().g()
,则情况会有所不同,但这会使依赖于在
静态方法调用之前执行的构造函数的可疑设计变得更糟

这个问题应该在框架方面解决,这两方面都在做,实例化
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()

这不是一个好的设计。无论是谁调用,方法的行为都应该相同。如果需要不同的行为,请为方法提供一个指定该行为的参数。在您的情况下,您可能希望调用者将配置文件或配置对象的路径作为参数传递。我的类之间有很多复杂的关系,为了简化,我在这里忽略了它们。实际上,类A扩展了Z,我的所有配置设置都应该在Z的构造函数中完成(相当于我的g()方法)。花时间修复它是值得的。调试这样的代码将是一场噩梦。但如果您真的不能或不想更改它,我建议您创建一个全局静态对象。如果您使用多个线程,则需要使用同步或锁来保护此全局对象。如果在构造函数之前调用多个类似的函数会怎么样?但无论如何,您需要在“g”方法中调用此代码,然后将类型存储在某种静态状态,如@VGR建议的状态。此外,在代码中还应该使用
方法.isAnnotationPresent(Marker1.class)
而不是这不是一个好的设计。无论是谁调用,方法的行为都应该相同。如果需要不同的行为,请为方法提供一个指定该行为的参数。在您的情况下,您可能希望调用者将配置文件或配置对象的路径作为参数传递。我的类之间有很多复杂的关系,为了简化,我在这里忽略了它们。实际上,类A扩展了Z,我的所有配置设置都应该在构造函数中完成