Java 尝试通过反射访问内部类构造函数的参数注释时发生ArrayIndexOutOfBoundsException

Java 尝试通过反射访问内部类构造函数的参数注释时发生ArrayIndexOutOfBoundsException,java,reflection,java-8,aspectj,Java,Reflection,Java 8,Aspectj,我正在尝试使用简单的自定义@NotNull注释对我的方法执行null检查,例如。 我将该方法声明为myMethod(@NotNull String name,String description),当有人调用该方法时,将null值作为'name'参数传递,并引发异常 我已经使用aspectj实现了一个简单方面。这个解决方案对我很有效。一个例外是内部类的构造函数。在这种情况下,由于java.lang.reflect.Parameter内的异常,方面崩溃: Exception in thread "

我正在尝试使用简单的自定义@NotNull注释对我的方法执行null检查,例如。 我将该方法声明为
myMethod(@NotNull String name,String description)
,当有人调用该方法时,将null值作为'name'参数传递,并引发异常

我已经使用aspectj实现了一个简单方面。这个解决方案对我很有效。一个例外是内部类的构造函数。在这种情况下,由于java.lang.reflect.Parameter内的异常,方面崩溃:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
    at java.lang.reflect.Parameter.getDeclaredAnnotations(Parameter.java:305)
    at java.lang.reflect.Parameter.declaredAnnotations(Parameter.java:342)
    at java.lang.reflect.Parameter.getAnnotation(Parameter.java:287)
    at java.lang.reflect.Parameter.getDeclaredAnnotation(Parameter.java:315)
    at ValidationAspect.checkNotNullArguments(ValidationAspect.java:22)
    at OuterClass$InnerClass.<init>(OuterClass.java:4)
    at OuterClass.constructInnerClass(OuterClass.java:14)
    at Main.main(Main.java:5)
测试等级:

公共类外部类{
公共类内部类{
公共内部阶级(
@NotNull字符串名称
) {
System.out.println(String.format(“构造名为:%s,name的内部类”);
}
}
公共内部类构造函数内部类(
字符串名
) {
返回新的内部类(名称);
}
}
用法:

公共类主{
公共静态void main(字符串[]args){
OuterClass outObj=新的OuterClass();
outObj.constructInnerClass(“myName”);
}
}
据我所知,这是由java将封闭类对象作为第一个参数传递给内部类的构造函数(我被告知这是标准行为)引起的。问题是,
params[i].executable.getParameterAnnotations()
似乎不知道附加参数,只返回“正常”参数的注释

我觉得这是aspectj或java.lang.reflection中的一个bug。但由于我找不到任何关于这方面的bug报告,我觉得我更可能是做错了什么。该应用程序运行在java 8(尝试了oracle jdk和上一个openjkd版本的多个不同版本)和aspectj 1.8.13(但也尝试了1.9.4)上

所以我的问题是:这是一个已知的bug吗?我的实现中是否存在一些缺陷?有解决办法吗?(我想手动将注释与参数匹配并不难,但由于我对java反射的了解非常有限,因此我无法真正预见其后果)


编辑:提供了工作示例

好吧,我太好奇了,玩我自己的MCVE。我可以排除AspectJ是罪魁祸首,并将问题归结为JDK/JRE问题:

内部(非静态)类构造函数的问题是,它们的第一个参数始终是外部对象的实例。Java8——我在1.8.0_152和1.8.0_211中都尝试过——包含一个bug的反射。基本上,它将实际内部构造函数参数的注释上移一个索引,例如,第一个构造函数参数的注释参数存储在索引0中,该索引实际上应该包含外部对象实例的注释。我的示例代码更好地解释了这一点,我想:

package de.scrum\u master.app;
导入静态java.lang.annotation.ElementType.PARAMETER;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
导入java.lang.annotation.Retention;
导入java.lang.annotation.Target;
@保留(运行时)
@目标(参数)
public@interface NotNull{}
package de.scrum\u master.app;
导入java.lang.annotation.annotation;
导入java.lang.reflect.Constructor;
导入java.lang.reflect.Parameter;
公共类应用程序{
班级内部{
公共内部(@NotNull字符串文本){
System.out.println(“用“+文本构建内部”);
}
}
公共静态void main(字符串[]args)抛出NoSuchMethodException、SecurityException{
构造函数=internal.class.getConstructor(Application.class,String.class);
System.out.println(构造器);
for(参数:constructor.getParameters()){
System.out.println(“+”参数);
对于(注释:parameter.getAnnotations())
System.out.println(“+”注释);
}
}
}
这重现了JDK 8的问题:

public de.scrum\u master.app.Application$Inner(de.scrum\u master.app.Application,java.lang.String)
de.scrum_master.app.Application arg0
@de.scrum_master.app.NotNull()
java.lang.String arg1
线程“main”java.lang.ArrayIndexOutOfBoundsException中出现异常异常:1
位于java.lang.reflect.Parameter.getDeclaredAnnotations(Parameter.java:305)
位于java.lang.reflect.Parameter.getAnnotations(Parameter.java:333)
位于de.scrum_master.app.Application.main(Application.java:19)
但是,如果您使用JDK 11(我使用的是11.0.2)运行,那么一切都会按照预期运行,如果我使用的方面带有类似您的建议:

public de.scrum\u master.app.Application$Inner(de.scrum\u master.app.Application,java.lang.String)
de.scrum_master.app.Application arg0
java.lang.String arg1
@de.scrum_master.app.NotNull()

我没有费心去看所有的JDK发行说明,以确定这是故意的还是偶然的,是哪个JDK版本(9、10、11),但至少我可以告诉你,在更新JDK 11之后,你应该没事。

好吧,我太好奇了,玩了我自己的MCVE。我可以排除AspectJ是罪魁祸首,并将问题归结为JDK/JRE问题:

内部(非静态)类构造函数的问题是,它们的第一个参数始终是外部对象的实例。Java8——我在1.8.0_152和1.8.0_211中都尝试过——包含一个bug的反射。基本上,它将实际内部构造函数参数的注释上移一个索引,例如,第一个构造函数参数的注释参数存储在索引0中,该索引实际上应该包含外部对象实例的注释。我的示例代码更好地解释了这一点,我想:

package de.scrum\u master.app;
导入静态java.lang.annotation.ElementType.PARAMETER;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
导入java.lang.annotation.Retention;
接口信息处理器