【问题标题】:How to get annotations of a caller method from another class如何从另一个类中获取调用者方法的注释
【发布时间】:2019-09-23 21:49:02
【问题描述】:

我正在使用jdk1.8.0_221,我想根据调用两种不同类型的方法在超类的构造函数中加载特定配置。我正在寻求最佳实践,最好是一种方便的方法。

为了简化,我把代码情况模拟成如下sn-p:

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() 方法不一定是静态的。

我尝试了以下 sn-p 但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;
         }
     }
}

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

【问题讨论】:

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

标签: java reflection java-8 annotations stack-trace


【解决方案1】:

你得到的结果,正是告诉你正在发生的事情。您的两个方法都没有调用构造函数。

你所有的方法都有这种形式

@Test
@MarkerN
public void fX() {
    Z.g();
}

因此它们包含对Z 类的static 方法g() 的调用。但没有Z 的实例化。

相反,您的类ABZ 的子类。由于您没有为它们声明显式构造函数,因此编译器会为它们生成默认构造函数,例如

public class A extends Z {

    public A() {
        super(); // here, the constructor of Z gets called
    }


// the other methods …

}

因此,反射查找会告诉您 Z 的构造函数的调用发生在名为 &lt;init&gt; 的方法中,该方法是 A 的构造函数,resp。 BB 的构造函数。

如果方法执行new Z().g() 会有所不同,但这会使依赖于在static 方法调用之前执行的构造函数的可疑设计变得更糟。

这个问题应该在框架端解决,它会同时实例化AB 并在它们上调用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()

【讨论】:

    猜你喜欢
    • 2018-02-23
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多