java 使用注解修改方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4851429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 08:19:06  来源:igfitidea点击:

Modify a method using Annotations

javareflectionannotations

提问by

How can I change what a method is doing in Java ?

如何更改方法在 Java 中的作用?

I mean, I am trying to use annotations to make the following code

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

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

}

Into

进入

public class TestClass
{
    private static StaticReference z;

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

}

This is a very simplified example of what I am trying to do. Anno1will have many possible combinations, but this is not my problem so far. My problem is how to add code to method test()

这是我正在尝试做的一个非常简化的例子。Anno1将有许多可能的组合,但这不是我目前的问题。我的问题是如何向方法添加代码test()

I am looking for a more generic solution if possible. Eg. A way to add every kind of code in the method (not just a way to .invokeToAll())

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

So far I am using import javax.annotation.processing.*;and I have the following code, but I don't know how to go on from there

到目前为止我正在使用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);               
        }           
    }
}

I have found something about Java Reflection but I have not found any source to help me with what I am doing.

我找到了一些关于 Java 反射的东西,但我没有找到任何可以帮助我完成我正在做的事情的来源。

Obviously I extends AbstractProcessorin my code

显然我extends AbstractProcessor在我的代码中

I have found this tutorial (http://www.zdnetasia.com/writing-and-processing-custom-annotations-part-3-39362483.htm) But this concerns creating a new class, not just changing a method. and the javax.lang.model.elementsdo not provide any way of editing that element (which in my case represents a Method).

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

I hope my question is clear and inline with the rules. If not please comment and I will clarify. Thanks.

我希望我的问题清楚并且符合规则。如果不是,请发表评论,我会澄清。谢谢。

采纳答案by Maxym

Annotation processing is wrong way to go for you, from Wikipedia:

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

When Java source code is compiled, annotations can be processed by compiler plug-ins called annotation processors. Processors can produce informational messages or create additional Java source files or resources, which in turn may be compiled and processed, but annotation processors cannot modify the annotated code itself.

在编译 Java 源代码时,注释可以由称为注释处理器的编译器插件进行处理。处理器可以生成信息性消息或创建额外的 Java 源文件或资源,这些源文件或资源又可以被编译和处理,但注释处理器不能修改被注释的代码本身。

People suggested you right way - AOP. Specifically you can use AspectJ. "Quick result" way is (if you use Eclipse):

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

1) Install AJDT(AspectJ Development Tools)
2) Create AspectJ project and add there your classes and annotations
3) Create Aspect:

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();
    }


}

now you can execute a test and you will see that it works ;) AJDT compiles code for you automatically, so do not need any manual work to do, hope that's what you called "magic" ;)

现在你可以执行一个测试,你会看到它是有效的;) AJDT 会自动为你编译代码,所以不需要任何手动工作,希望这就是你所说的“魔法”;)

UPDATE:

更新:

if your code in test() method depends on Anno1 annotation value, then inside aspect you can get class annotation for which it is executed this way:

如果您在 test() 方法中的代码取决于 Anno1 注释值,那么在方面内部您可以获得以这种方式执行的类注释:

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();
    }

}

where thisJoinPointis a special reference variable.

其中thisJoinPoint是一个特殊的参考变量。

UPDATE2:

更新2:

if you want to add System.out.println( this )in your aspect, you need to write there System.out.println( thisJoinPoint.getThis() ), just tested and it works. thisJoinPoint.getThis()returns you "this" but not exactly; in fact this is Object variable and if you want to get any propery you need either to cast or to use reflection. And thisJoinPoint.getThis()does not provide access to private properties.

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

Well, now seems that your question is answered, but if I missed anything, or you get additional question/problems with this way - feel free to ask ;)

好吧,现在看来您的问题已得到解答,但是如果我错过了任何内容,或者您​​通过这种方式遇到了其他问题/问题 - 随时提问;)

回答by Norswap

It's perfectly possible to do what you ask, although there is a caveat: relying on private compiler APIs. Sounds scary, but it isn't really (compiler implementations tend to be stable).

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

There's a paper that explains the procedure: The Hacker's Guide to Javac.

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

Notably, this is used by Project Lombokto provide automatic getter/setter generation (amongst other things). The following articleexplains how it does it, basically re-iterating what is said the aforementioned paper.

值得注意的是,Project Lombok 使用它来提供自动 getter/setter 生成(除其他外)。在下面的文章解释了它是怎么做的,基本上重新迭代说什么了上述文件。

回答by Johan Sj?berg

Well, you might see if the following boilerplate code will be useful:

好吧,您可能会看到以下样板代码是否有用:

public void magic(Object bean, String[] args) throws Exception {
    for (Method method : bean.getClass().getDeclaredMethods()) {
        if (method.isAnnotationPresent(Anno2.class)) {
            // Invoke the original method
            method.invoke(bean, args);
            // Invoke your 'z' method
            StaticReference.invokeAll();
        }
    }
}

As an alternative your might employ aspect oriented programming, for instance you have the AspectJproject.

作为替代方案,您可以使用面向方面的编程,例如您有AspectJ项目。

回答by Andreas Dolk

I'm not sure at all if it is even possible to change the source or byte code via annotations. From what your describing it looks as if aspect oriented programmingcould provide a solution to your problem.

我完全不确定是否可以通过注释更改源代码或字节码。从您的描述来看,面向方面的编程似乎可以为您的问题提供解决方案。

Your annotations are pretty similiar to the pointcutconcept (they mark a location where code needs to be inserted) and the inserted code is close the adviceconcept.

您的注释与切入点概念非常相似(它们标记需要插入代码的位置)并且插入的代码接近建议概念。

Another approach would be parsing the java source file into an abstract syntax tree, modify this AST and serialize to a java compiler input.

另一种方法是将 java 源文件解析为抽象语法树,修改此 AST 并序列化为 java 编译器输入。

回答by Landei

If your class extends a suitable interface, you could wrap it in a DynamicProxy, which delegates all calls to the original methods, except the call to test.

如果你的类扩展了一个合适的接口,你可以将它包装在一个 DynamicProxy 中,它将所有调用委托给原始方法,除了对 test 的调用。