Я пытаюсь выполнить нулевые проверки моих методов, используя простую пользовательскую аннотацию @NotNull, т.е. я объявляю метод как myMethod(@NotNull String name, String description)
, и когда кто-то вызывает этот метод с нулевым значением, переданным в качестве аргумента '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)
Упрощенная реализация:
Аспект:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.ConstructorSignature;
import java.lang.reflect.Parameter;
@Aspect
public class ValidationAspect {
@Pointcut("execution(*.new(.., @NotNull (*), ..))")
private void anyConstructorWithNotNullParam() {}
@Before("anyConstructorWithNotNullParam()")
public void checkNotNullArguments(JoinPoint joinPoint) {
ConstructorSignature signature = (ConstructorSignature) joinPoint.getSignature();
Object[] args = joinPoint.getArgs();
Parameter[] params = signature.getConstructor().getParameters();
for(int i = 0; i < args.length; i++) {
if(params[i].getDeclaredAnnotation(NotNull.class) != null) {
if (args[i] == null) {
throw new IllegalArgumentException("Illegal null argument");
}
}
}
}
}
Аннотация:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER})
public @interface NotNull { }
Тестовый класс:
public class OuterClass {
public class InnerClass {
public InnerClass(
@NotNull String name
) {
System.out.println(String.format("Construct inner class with name: %s", name));
}
}
public InnerClass constructInnerClass(
String name
) {
return new InnerClass(name);
}
}
Использование:
public class Main {
public static void main(String[] args) {
OuterClass outObj = new OuterClass();
outObj.constructInnerClass("myName");
}
}
Насколько я могу судить, это вызвано передачей Java объекта объекта класса какпервый аргумент для конструктора внутреннего класса (который мне сказали, это стандартное поведение).Проблема в том, что params[i].executable.getParameterAnnotations()
, похоже, не знает о дополнительном аргументе и возвращает аннотации только для "нормальных" параметров
Мне кажется, что это ошибка в aspectj или java.lang.reflection.Но так как я не могу найти сообщение об ошибке, мне кажется, что я делаю что-то не так.Приложение работает на Java 8 (пробовал несколько разных сборок oracle jdk и последней сборки openjkd) и aspectj 1.8.13 (но пробовал также 1.9.4).
Так что мой вопрос (а): этоизвестная ошибка?Есть ли какой-то недостаток в моей реализации?Есть ли обходной путь?(Полагаю, было бы не сложно сопоставить аннотации с параметрами вручную. Но поскольку у меня очень ограниченные знания об java-отражении, я не могу предвидеть последствия).
Отредактировано: при условии работыпример