Community
Participate
Working Groups
Here is a typical scenario for using annotation matching: // a trace level enum Level { NONE,ONE,TWO,THREE; } // a trace annotation @interface Trace { Level value(); } @Trace(Level.ONE) public void c() { } The annotation is being used to configure the trace level of the method. The AspectJ code that binds this is as follows: before(Trace t): execution(* *(..)) && @annotation(t) { callTrace(thisJoinPoint,t.value()); } The advice only needed the enum value from the annotation and that was statically determinable. The code generated for the above case right now is sub-optimal, there are a couple of reflection calls: Code: Stack=4, Locals=1, Args_size=1 0: invokestatic #51; //Method X.aspectOf:()LX; 3: ldc #1; //class Example 5: ldc #54; //String c 7: iconst_0 8: anewarray #56; //class java/lang/Class 11: invokevirtual #60; //Method java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; 14: ldc #53; //class Trace 16: invokevirtual #66; //Method java/lang/reflect/Method.getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; 19: checkcast #53; //class Trace 22: invokevirtual #70; //Method X.ajc$before$X$1$457fe24b:(LTrace;)V 25: return Under this enhancement I'll improve that code generation for this scenario
Changes are in. New syntax construct: before(Trace t): execution(* *(..)) && @annotation(t) { callTrace(thisJoinPoint,t.value()); } becomes: before(Level lev): execution(* *(..)) && @annotation(Trace(lev)) { callTrace(thisJoinPoint,lev); } and the generated code becomes: Code: Stack=2, Locals=1, Args_size=1 0: invokestatic #28; //Method X.aspectOf:()LX; 3: getstatic #32; //Field Level.ONE:LLevel; 6: invokevirtual #36; //Method X.ajc$before$X$1$4fa4f687:(LLevel;)V 9: return rather more optimal and no reflection. Now I could have modified the compiler to detect that only the Level within the annotation was being accessed within the advice and automatically changed the generated advice signature and code to do the same as the above. But I wonder if that is too much magic? Also this ONLY works for method-execution join points AND enumerated type values within annotations. Optimizing for cases other than method-execution will, I think, cause problems with coupling as our advised locations are then tightly coupled to the annotation value. For example, if a call joinpoint was optimized in this way and the annotation on the called entity was changed, the advised location would also need recompilation or it would not see the change. We don't have this problem for method-execution as a change to the annotation value and rebuild will also cause the advised location to be updated too. So using this optimized syntax in the wrong way will currently produce 'compiler limitation' messages. So, in fact, to implement this at all join points correctly (removing the method-execution restriction) would just mean inserting the same reflection code and an additional field access before calling the advice. I would address supporting this syntax as other join points and for other annotation value types as and when use cases arise.