The static initializer
In our aspects we refer to the join point object in two places - in ExceptionTracer.aj and in FactoryIntercept.aj. Corresponding to these references, there are two private static fields of type org.aspectj.lang.JoinPoint$StaticPart injected in to the class.
private static final org.aspectj.lang.JoinPoint$StaticPart ajc$tjp_0;These fields are initialized in the static initializer of the class, which calls helper methods in aspectj to construct the JoinPoint$StaticPart objects.
private static final org.aspectj.lang.JoinPoint$StaticPart ajc$tjp_1;
Code injection
- Public methods and variable names are retained after being injected. So they can be accessed later using reflection.
- The injected methods are just wrappers that call the actual body - a static method in the aspect class.
public int getCalls();
Code:
0: aload_0
1: invokestatic #110; //Method ajtest/aspects/AroundAndInject.ajc$interMethod$ajtest_aspects_AroundAndInject$ajtest_java_Test$getCalls:(Lajtest/java/Test;)I
4: ireturn
public void incCalls();
Code:
0: aload_0
1: invokestatic #102; //Method ajtest/aspects/AroundAndInject.ajc$interMethod$ajtest_aspects_AroundAndInject$ajtest_java_Test$incCalls:(Lajtest/java/Test;)V
4: return - Private injected variables are declared as public, but with an obfuscated name. So they can not be accessed with their original names through reflection. Why public? Because we have an aspect on the field access join point and the aspect needs to access this field from within the aspect code!
public int ajc$interField$ajtest_aspects_AroundAndInject$nCalls;
We test the FieldAccess aspect around the join points involving get of fld1 in our testFieldAccessAspect method. Look at the source code and you can see that we read fld1 thrice in the testFieldAccessAspect method - once to print it, then to increment it by 1 and then again to print it. Now take a look at the modified bytecode of the woven testFieldAccessAspect method in the javap output.
- At the first instance where we read the field, instead of directly fetching the field, now there is a call to the around advice, a method fld1_aroundBody1$advice, to get the value.
invokestatic #144; //Method fld1_aroundBody1$advice:(Lajtest/aspects/FieldAccess;Lorg/aspectj/runtime/internal/AroundClosure;)I
- The advice method, in turn, invokes another method "private static final int fld1_aroundBody0()" when it needs to access the field value. This method accesses the field directly through a getstatic instruction.
- At the second and third instances where we read the field again, the same happens, but to a different set of methods.
invokestatic #150; //Method fld1_aroundBody3$advice:(Lajtest/aspects/FieldAccess;Lorg/aspectj/runtime/internal/AroundClosure;)I
invokestatic #152; //Method fld1_aroundBody2:()I
and
invokestatic #156; //Method fld1_aroundBody5$advice:(Lajtest/aspects/FieldAccess;Lorg/aspectj/runtime/internal/AroundClosure;)I
invokestatic #158; //Method fld1_aroundBody4:()I - The three sets of methods are identical. And they are copies from the FieldAccess aspect class bytecode method ajc$around$ajtest_aspects_FieldAccess$1$32f71218. So the weaver has been picking up the bytecode from the aspect class method and injecting new advice methods into the woven class.
- The "ajc$around$ajtest_aspects_FieldAccess$1$32f71218proceed" method in the FieldAccess aspect class is however ignored in this case. It would have been used to chain aspects if I had multiple aspects on the same join point.
- The reason behind multiple identical methods generated for the advice however beats me. If you have any explanations/suggestions, I'll be glad to hear.
Advices 'around' a method call
We test the an aspect around a method call in the call to the "doSyso" method in main. The story here is very similar to the behavior above.
- There are two methods injected into the Test class for each instance of the call to doSyso. The methods injected for the first instance of the call are:
private static final void doSyso_aroundBody7$advice(ajtest.java.Test, java.lang.String, ajtest.aspects.AroundAndInject, ajtest.java.Test, java.lang.String, org.aspectj.runtime.internal.AroundClosure);
private static final void doSyso_aroundBody6(ajtest.java.Test, java.lang.String); - The method call at the join point is replaced to call the advice doSyso_aroundBody7$advice.
- The weaver copies code from
public void ajc$around$ajtest_aspects_AroundAndInject$1$38b5b4f8(ajtest.java.Test, java.lang.String, org.aspectj.runtime.internal.AroundClosure);
in the AroundAndInject aspect class intoprivate static final void doSyso_aroundBody7$advice(ajtest.java.Test, java.lang.String, ajtest.aspects.AroundAndInject, ajtest.java.Test, java.lang.String, org.aspectj.runtime.internal.AroundClosure);
in the Test class. - The advice method in turn calls the second generated method doSyso_aroundBody6 to actually call the method.
invokespecial #21; //Method doSyso:(Ljava/lang/String;)V
- Again, the reason behind multiple identical methods generated for the advice beats me.
Advices 'around' a method execution
We test the an aspect around a method call in the call to the "doSysoExec" method in main. This is also similar to the behavior above, except:
- The call to doSysoExec method is retained as it is.
- The body of the doSysoExec method is replaced with a call to an injected advice method:
invokestatic #306; //Method doSysoExec_aroundBody13$advice:(Lajtest/java/Test;Ljava/lang/String;Lajtest/aspects/AroundAndInject;Lajtest/java/Test;Ljava/lang/String;Lorg/aspectj/runtime/internal/AroundClosure;)V
- The injected advice method in turn calls another advice method
invokestatic #308; //Method doSysoExec_aroundBody12:(Lajtest/java/Test;Ljava/lang/String;)V
which actually contains what the original doSysoExec method hadprivate static final void doSysoExec_aroundBody12(ajtest.java.Test, java.lang.String);
Code:
0: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_1
4: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return - Multiple advice methods are not injected for multiple calls to the method - obviously since we are not interested in the 'calls' (which are at multiple places), but in execution (which is in one method).
Advices 'before' and 'after'
The testBeforeAfterAspect method in the Test class tests the before and after aspects.
- The advice code is a method in the aspect class.
- Calls are made to the advice methods before and after the join pont.
invokevirtual #176; //Method ajtest/aspects/BeforeAfterIntercept.ajc$before$ajtest_aspects_BeforeAfterIntercept$1$218e91dd:()V
and
invokevirtual #179; //Method ajtest/aspects/BeforeAfterIntercept.ajc$after$ajtest_aspects_BeforeAfterIntercept$2$218e91dd:()V - Since our aspect was for an "after" join point, it implicitly meant both "after returning" and "after throwing". And the weaver injected an exception handler to do the job.
We also used a before advice for the exception handler advice and the code generation is similar.
We got some insights into what really happens when AspectJ weaves our aspects into the code. Hopefully, it will help us designing our aspects better. In the next post we'll see what happens under the hood when we use different aspect instantiation models.
No comments:
Post a Comment