Question

Is it possible to do Inter Type Declarations with AspectJ on Compiled Class Files at Load Time Weaving?

As an example: I compile some Groovy code and want to add fields or methods with IDT.

Was it helpful?

Solution

Update:

Oh my goodness, you do not need reflection to access members or execute methods. Eclipse shows errors in the editor, but you may just ignore them, the code compiles and runs fine anyway. So the aspect is really much more strightforward and simple:

public aspect LTWAspect {
    public static String Application.staticField = "value of static field";
    public String Application.normalField = "value of normal field";

    public void Application.myMethod() {
        System.out.println(normalField);
    }

    void around() : execution(void Application.main(..)) {
        System.out.println("around before");
        proceed();
        System.out.println("around after");
        System.out.println(Application.staticField);
        new Application().myMethod();
    }
}

Original answer:

Yes, but you have a hen-and-egg problem there, i.e. you cannot just reference the newly introduced fields from your LTW aspect code without reflection. (The last sentence is not true, see update above.) Plus, in order to make your LTW aspect compile, you need the classes to be woven on the project's build path so as to be able to reference them. Example:

Java project

public class Application {
    public static void main(String[] args) {
        System.out.println("main");
    }
}

AspectJ project

import org.aspectj.lang.SoftException;

public aspect LTWAspect {
    public static String Application.staticField = "value of static field";
    public String Application.normalField = "value of normal field";

    public void Application.myMethod() {
        try {
            System.out.println(Application.class.getDeclaredField("normalField").get(this));
        } catch (Exception e) {
            throw new SoftException(e);
        }
    }

    void around() : execution(void Application.main(..)) {
        System.out.println("around before");
        proceed();
        System.out.println("around after");
        try {
            System.out.println(Application.class.getDeclaredField("staticField").get(null));
            Application.class.getDeclaredMethod("myMethod", null).invoke(new Application());
        } catch (Exception e) {
            throw new SoftException(e);
        }
    }
}

So, e.g. in Eclipse you need to put the Java project on the AspectJ project's build path under "Projects" because only then it can see Java class Application on which you want to declare members. After compilation you just start the Java project and do LTW on the aspect project (don't forget an aop-ajc.xml referencing LTWAspect).

In my example above I declare a static member, a non-static ("normal") member and a non-static method. My advice prints the static member and calls the non-static method, both via reflection. The non-static method then prints the non-static member, again via reflection. This is not nice, but it works and proves the ITD in combination with LTW is possible. There might be a more elegant way, but if so I am unaware of it. (Update: There is a more elegant way: Just ignore the errors marked by Eclipse IDE, see above.)

Program output

around before
main
around after
value of static field
value of normal field
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top