使用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不提供任何编辑该元素的方法(在我的例子中代表一个Method)。

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

从维基百科开始,注释处理是错误的方法:

编译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()返回“this”但不完全; 实际上这是对象变量,如果你想获得任何属性,你需要强制转换或使用reflection。 并且thisJoinPoint.getThis()不提供对私有属性的访问。

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

尽管有一点需要注意:依赖私有编译器API,完全可以做你所要求的。 听起来很可怕,但事实并非如此(编译器实现趋于稳定)。

有一篇文章解释了程序: 黑客的Javac指南 。

值得注意的是, Project Lombok使用它来提供自动getter / setter生成(以及其他function)。 下面的文章解释了它是如何做到的,基本上重复了上述论文的内容。

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

 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项目。

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

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

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

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