Muggen Muggen -4 years ago 110
Java Question

Modify a method using Annotations

How can I change what a method is doing in 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.
Anno1
will have many possible combinations, but this is not my problem so far. My problem is how to add code to method
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()
)

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

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.

Obviously I
extends AbstractProcessor
in my code

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.elements
do not provide any way of editing that element (which in my case represents a Method).

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

Answer Source

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.

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

1) Install AJDT (AspectJ Development Tools)
2) Create AspectJ project and add there your classes and annotations
3) Create 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" ;)

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:

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 thisJoinPoint is a special reference variable.

UPDATE2:

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.

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

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download