使用批注修改方法

2022-09-04 08:06:23

如何更改方法在 Java 中执行的操作?

我的意思是,我正在尝试使用注释来制作以下代码

@Anno1(Argument = "Option1")
public class TestClass
{       
    @Anno2
    public void test()
    {
    }

}

public class TestClass
{
    private static StaticReference z;

    public void test()
    {
           z.invokeToAll();
    }

}

这是我试图做的一个非常简化的例子。 将有许多可能的组合,但到目前为止这不是我的问题。我的问题是如何将代码添加到方法Anno1test()

如果可能的话,我正在寻找一个更通用的解决方案。例如。一种在方法中添加每种代码的方法(而不仅仅是一种方法.invokeToAll())

到目前为止,我正在使用,我有以下代码,但我不知道如何从那里继续import javax.annotation.processing.*;

private void processMethodAnnotations(RoundEnvironment env)
{
    for (Element e : env.getElementsAnnotatedWith(Anno2.class))
    {
        //If it is a valid annotation over a method
        if (e.getKind() == ElementKind.METHOD) 
        {
            //What to do here :S
        }else
        {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,"Not a method!", e);               
        }           
    }
}

我发现了一些关于Java Reflection的东西,但我还没有找到任何资源来帮助我完成我正在做的事情。

显然我在我的代码中extends AbstractProcessor

我发现本教程(http://www.zdnetasia.com/writing-and-processing-custom-annotations-part-3-39362483.htm)但是这涉及创建新类,而不仅仅是更改方法。并且不提供任何编辑该元素的方法(在我的情况下代表一个方法)。javax.lang.model.elements

我希望我的问题很清楚,符合规则。如果没有,请发表评论,我会澄清。谢谢。


答案 1

注释处理对你来说是错误的方式,来自维基百科

编译 Java 源代码时,注释可由称为注释处理器的编译器插件进行处理。处理器可以生成信息性消息或创建其他 Java 源文件或资源,这些文件或资源反过来也可以进行编译和处理,但注释处理器不能修改带注释的代码本身。

人们建议你是正确的方式 - AOP。具体来说,您可以使用AspectJ。“快速结果”的方式是(如果你使用Eclipse):

1)安装AJDT(AspectJ开发工具)
2)创建AspectJ项目并在那里添加您的类和注释
3)创建Aspect:

public aspect Processor {

    private StaticReference z;

    pointcut generic()
            // intercept execution of method named test, annotated with @Anno1
            // from any class type, annotated with @Anno2
        : execution(@Anno2 * (@Anno1 *).test())
            // method takes no arguments
        && args ();

    // here you have write what you want method actually does
    void around () : generic()  {
        z.invokeToAll();
    }


}

现在你可以执行一个测试,你会看到它的工作原理;)AJDT会自动为您编译代码,因此不需要任何手动工作即可完成,希望这就是您所说的“魔术”;)

更新:

如果你的 test() 方法中的代码依赖于 Anno1 注释值,那么在 aspect 内部,你可以得到以这种方式执行它的类注释:

void around () : generic()  {

    Annotation[] classAnnotations = thisJoinPoint.getThis().getClass().getAnnotations();

    String ArgumentValue = null;
    for ( Annotation annotation : classAnnotations ) {
        if ( annotation instanceof Anno1 ) {
            ArgumentValue = ((Anno1) annotation).Argument(); 
            break;
        }
    }

    if ( ArgumentValue != null && ArgumentValue.equals("Option1")) {
        z.invokeToAll();
    }

}

其中 thisJoinPoint 是一个特殊的引用变量。

更新2:

如果你想添加你的方面,你需要写在那里,只是测试过,它的工作原理。 返回“这个”,但不完全是;事实上,这是对象变量,如果你想获得任何属性,你需要转换或使用反射。并且不提供对私有属性的访问。System.out.println( this )System.out.println( thisJoinPoint.getThis() )thisJoinPoint.getThis()thisJoinPoint.getThis()

好吧,现在看来你的问题得到了回答,但是如果我错过了什么,或者你以这种方式得到了额外的问题/问题 - 请随时提出;)


答案 2

完全可以按照您的要求去做,尽管有一个警告:依赖私有编译器API。听起来很可怕,但事实并非如此(编译器实现往往是稳定的)。

有一篇论文解释了这个过程:The Hacker's Guide to Javac

值得注意的是,龙目岛项目使用它来提供自动获取器/设置器生成(以及其他功能)。下面的文章解释了它是如何做到的,基本上是重复上述论文所说的内容。