使用Annotations修改方法

时间:2011-01-31 13:34:45

标签: java reflection annotations

如何更改Java中的方法?

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

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

}

public class TestClass
{
    private static StaticReference z;

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

}

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

如果可能,我正在寻找更通用的解决方案。例如。一种在方法中添加各种代码的方法(不仅仅是.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没有提供任何编辑该元素的方法(在我的例子中代表一个方法)。

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

4 个答案:

答案 0 :(得分:12)

Wikipedia

开始,注释处理是错误的方法
  

编译Java源代码时,   注释可以通过   编译器插件称为注释   处理器。处理器可以生产   信息性消息或创建   其他Java源文件或   资源,反过来可能是   编译和处理,但注释   处理器无法修改注释   代码本身。

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

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

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注释值,那么在内部方面,您可以获得以这种方式执行的类注释:

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是一个特殊的引用变量。

<强> UPDATE2:

如果你想在你的方面添加System.out.println( this ),你需要在那里写System.out.println( thisJoinPoint.getThis() ),只是经过测试并且有效。 thisJoinPoint.getThis()会向您返回“此”但不完全相同;实际上这是对象变量,如果你想获得任何属性,你需要强制转换或使用反射。 thisJoinPoint.getThis()不提供对私有财产的访问权。

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

我根本不确定是否可以通过注释更改源代码或字节代码。从您的描述看起来好像aspect oriented programming可以为您的问题提供解决方案。

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

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

答案 3 :(得分:0)

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