我使用的AspectJ 1.8.8编译时编织,我有这样一个块
@SomeAnnotation(value="someValue")
public List doSomething(String someArg) {
...
}
其中@SomeAnnotation
与一个“绕”的建议执行。
寻找与JD-GUI字节码,我看到以下生成的代码(略格式化的):
public class SomeClass {
private static Annotation ajc$anno$5;
...
@SomeAnnotation(value="someValue")
public List doSomething(String someArg)
{
String str = someArg;
JoinPoint localJoinPoint = Factory.makeJP(ajc$tjp_5, this, this, str);
Object[] arrayOfObject = new Object[3];
arrayOfObject[0] = this;
arrayOfObject[1] = str;
arrayOfObject[2] = localJoinPoint;
Annotation tmp56_53 = ajc$anno$5;
if (tmp56_53 == null) {
tmp56_53;
}
return (List)new SomeClass.AjcClosure11(arrayOfObject).linkClosureAndJoinPoint(69648).around(tmp56_53, (SomeAnnotation)(ajc$anno$5 = SomeClass.class.getDeclaredMethod("doSomething", new Class[] { String.class }).getAnnotation(SomeAnnotation.class)));
}
}
我想知道为什么条件(if (tmp56_53...)
)即使存在,因为它似乎什么都不做(并且也是语法不正确的Java?也许是因为这是由浓缩苹果汁产生的?)。我很好奇,是因为它是在一个覆盖工具(JaCoCo)造成“分支未命中”。
这里是javap的原始Java的机器代码:
0: aload_1
1: astore_2
2: getstatic #480 // Field ajc$tjp_10:Lorg/aspectj/lang/JoinPoint$StaticPart;
5: aload_0
6: aload_0
7: aload_2
8: invokestatic #312 // Method org/aspectj/runtime/reflect/Factory.makeJP:(Lorg/aspectj/lang/JoinPoint$StaticPart;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Lorg/aspectj/lang/JoinPoint;
11: astore_3
12: invokestatic #339 // Method com/foo/SomeAspect.aspectOf:()Lcom/foo/SomeAspect;
15: iconst_3
16: anewarray #2 // class java/lang/Object
19: astore 4
21: aload 4
23: iconst_0
24: aload_0
25: aastore
26: aload 4
28: iconst_1
29: aload_2
30: aastore
31: aload 4
33: iconst_2
34: aload_3
35: aastore
36: new #484 // class com/foo/SomeClass$AjcClosure21
39: dup
40: aload 4
42: invokespecial #485 // Method com/foo/SomeClass$AjcClosure21."<init>":([Ljava/lang/Object;)V
45: ldc_w #327 // int 69648
48: invokevirtual #333 // Method org/aspectj/runtime/internal/AroundClosure.linkClosureAndJoinPoint:(I)Lorg/aspectj/lang/ProceedingJoinPoint;
51: getstatic #488 // Field ajc$anno$10:Ljava/lang/annotation/Annotation;
54: dup
55: ifnonnull 86
58: pop
59: ldc #75 // class com/foo/SomeClass
61: ldc_w #489 // String someArg
64: iconst_1
65: anewarray #348 // class java/lang/Class
68: dup
69: iconst_0
70: ldc #171 // class java/lang/String
72: aastore
73: invokevirtual #352 // Method java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
76: ldc_w #341 // class com/foo/SomeAnnotation
79: invokevirtual #358 // Method java/lang/reflect/Method.getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
82: dup
83: putstatic #488 // Field ajc$anno$10:Ljava/lang/annotation/Annotation;
86: nop
87: checkcast #341 // class com/foo/SomeAnnotation
90: invokevirtual #362 // Method com/foo/SomeAspect.around:(Lorg/aspectj/lang/ProceedingJoinPoint;Lcom/foo/SomeAnnotation;)Ljava/lang/Object;
93: pop
94: return
貌似ifnonnull
可能是有问题的条件,但我不熟悉,在所有JVM指令,我仍然不知道为什么会AspectJ的产生这样的逻辑。
TL;博士:这是一个正常的延迟初始化,并jd
只是困惑。
字节16是它创建new Object[3]
:
16: anewarray #2 // class java/lang/Object
之后,在19-35,它只是立即的局部变量拷贝到堆栈(iconst
的指数,aload
为参考),然后将它们写入到阵列(aastore
)可以看到。眼前的下一个字节是36,这是new
运营商(不仅仅是分配,然后紧接着是invokespecial
运行构造函数)。
这给我们带来了48字节,这就要求linkClosureAndJoinPoint
。你不包括你的常数表,但在45 ldc_w #327
加载恒定值69648,这样给我们带来了高达.around
点。
现在,一些有趣的事情发生在字节51单链呼叫jd
重建现已中断。字节码装入静态注释字段ajc$anno$10
(未5,如jd
说)压入堆栈。如果注释字段不为空(55),然后执行跳转到86(无操作,作为“着陆点”的跳转),它可完成铸检查((SomeAnnotation)
),最后实际上调用了意见。
多数民众赞成跳过的代码(58-82)说这一点,你会从反编译的认识:
SomeClass.class
.getDeclaredMethod("doSomething", new Class[] { String.class })
.getAnnotation(SomeAnnotation.class)
然后字节83中的结果存储到静磁场,并且执行从那里进行。
在Java方面,这正是发生了什么:
if (cachedAnnotation == null) {
cachedAnnotation = getAnnotationOnMethodUsingReflection();
}
AspectJ的字节码是很紧,清洁此处(可能是手工优化,因为这很可能是非常热的代码)。因为这或因为逻辑中断链接的方法调用,jd
是感到困惑和分裂空校验和分配无论是。