How do Java method annotations work in conjunction with method overriding? - java

I have a parent class Parent and a child class Child, defined thus:
class Parent {
#MyAnnotation("hello")
void foo() {
// implementation irrelevant
}
}
class Child extends Parent {
#Override
foo() {
// implementation irrelevant
}
}
If I obtain a Method reference to Child::foo, will childFoo.getAnnotation(MyAnnotation.class) give me #MyAnnotation? Or will it be null?
I'm interested more generally in how or whether annotation works with Java inheritance.

Copied verbatim from http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance:
Annotation Inheritance
It is important to understand the rules relating to inheritance of annotations, as these have a bearing on join point matching based on the presence or absence of annotations.
By default annotations are not inherited. Given the following program
#MyAnnotation
class Super {
#Oneway public void foo() {}
}
class Sub extends Super {
public void foo() {}
}
Then Sub does not have the MyAnnotation annotation, and Sub.foo() is not an #Oneway method, despite the fact that it overrides Super.foo() which is.
If an annotation type has the meta-annotation #Inherited then an annotation of that type on a class will cause the annotation to be inherited by sub-classes. So, in the example above, if the MyAnnotation type had the #Inherited attribute, then Sub would have the MyAnnotation annotation.
#Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.

You found your answer already: there is no provision for method-annotation inheritance in the JDK.
But climbing the super-class chain in search of annotated methods is also easy to implement:
/**
* Climbs the super-class chain to find the first method with the given signature which is
* annotated with the given annotation.
*
* #return A method of the requested signature, applicable to all instances of the given
* class, and annotated with the required annotation
* #throws NoSuchMethodException If no method was found that matches this description
*/
public Method getAnnotatedMethod(Class<? extends Annotation> annotation,
Class c, String methodName, Class... parameterTypes)
throws NoSuchMethodException {
Method method = c.getMethod(methodName, parameterTypes);
if (method.isAnnotationPresent(annotation)) {
return method;
}
return getAnnotatedMethod(annotation, c.getSuperclass(), methodName, parameterTypes);
}

Using Spring Core you can resolve with
AnnotationUtils.java

While the answer to the question as asked is that Java's Method.getAnnotation() does not consider overridden methods, sometimes it is useful to find these annotations. Here is a more complete version of Saintali's answer that I'm currently using:
public static <A extends Annotation> A getInheritedAnnotation(
Class<A> annotationClass, AnnotatedElement element)
{
A annotation = element.getAnnotation(annotationClass);
if (annotation == null && element instanceof Method)
annotation = getOverriddenAnnotation(annotationClass, (Method) element);
return annotation;
}
private static <A extends Annotation> A getOverriddenAnnotation(
Class<A> annotationClass, Method method)
{
final Class<?> methodClass = method.getDeclaringClass();
final String name = method.getName();
final Class<?>[] params = method.getParameterTypes();
// prioritize all superclasses over all interfaces
final Class<?> superclass = methodClass.getSuperclass();
if (superclass != null)
{
final A annotation =
getOverriddenAnnotationFrom(annotationClass, superclass, name, params);
if (annotation != null)
return annotation;
}
// depth-first search over interface hierarchy
for (final Class<?> intf : methodClass.getInterfaces())
{
final A annotation =
getOverriddenAnnotationFrom(annotationClass, intf, name, params);
if (annotation != null)
return annotation;
}
return null;
}
private static <A extends Annotation> A getOverriddenAnnotationFrom(
Class<A> annotationClass, Class<?> searchClass, String name, Class<?>[] params)
{
try
{
final Method method = searchClass.getMethod(name, params);
final A annotation = method.getAnnotation(annotationClass);
if (annotation != null)
return annotation;
return getOverriddenAnnotation(annotationClass, method);
}
catch (final NoSuchMethodException e)
{
return null;
}
}

Related

Get the value of an annotation with reflection (without knowing the concrete annotation type)

I have the following class:
public final class SomeClass {
#Signature("some info")
public void someMethod() {
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
#Inherited
private #interface Signature {
String value();
}
}
This class is not in my source code nor in a dependency, it's compiled from a resource file on-the-fly at runtime. That means, the symbol Signature.class (as well as SomeClass.class) do not exist in my class loader and will never do at compile time, so I can't do the easy:
Signature signature = method.getAnnotation(Signature.class);
signature.value();
I would like to retrieve the value that is assigned to the annotation #Signature at runtime, but I'm being unable to.
All I know is that the method will only have one annotation (the #Signature annotation) and that this annotation will always have one parameter value, but that's all I know.
This is my (raw) attempt:
Class<?> compiledClass = compiler.compile(classSourceCode); //this properly return the class instance
for (Method method : compiledClass.getDeclaredMethods()) {
for (Annotation annotation : method.getAnnotations()) {
Class<? extends Annotation> realType = annotation.annotationType();
for (Method annotationMethod : realType.getDeclaredMethods()) {
System.out.println(annotationMethod.invoke(annotation)); //<-- THIS FAILS
}
}
}
The failure that I get at runtime is:
java.lang.IllegalAccessException: class SomeClass cannot access a member of interface SomeClass$Signature with modifiers "public abstract"
The method that I get is the method value().
How can I do that?

How to get return type of annotated method in annotation processor?

I am learning to write custom annotations. I have a simple annotation that needs to verify if the return type of a method matches return type specified in the annotation. Below is the code.
Annotation code:
#Target(ElementType.METHOD)
public #interface ReturnCheck {
String value() default "void";
}
Annotation processor:
#SupportedAnnotationTypes("com.rajesh.customannotations.ReturnCheck")
public class ReturnCheckProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for ( Element element : roundEnv.getElementsAnnotatedWith(ReturnCheck.class) ) {
//Get return type of the method
}
return false;
}
}
I want to get the return type of the annotated method so that I can compare it with the value specified in the annotation.
How can I get the return type of the method ?
Here is what you need:
if (element.getKind() == ElementKind.METHOD) {
TypeMirror returnType = ((ExecutableElement) element).getReturnType();
// use returnType for stuff ...
}
Explanation:
You can check the ElementKind in order to dispatch on its concrete type. This is the recommended way to do it instead of instanceof. After that you know its an ExecutableElement and can cast it to one.
See ExecutableElement, and Element for more details.

javaslang List.of() on cdi Instance

I have multiple class with a Qualifier that I created:
#ServiceComponent(restPath = "/trucks")
public class TruckService {
}
#ServiceComponent(restPath = "/cars")
public class CarService {
}
here is the Qualifier (not important for the question)
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, FIELD})
public #interface ServiceComponent {
public boolean exposeAsRest() default true;
#Nonbinding public String restPath() default "";
#Nonbinding public String restGetPrefix() default "get,find,all";
#Nonbinding public String restPostPrefix() default "create,new,post";
}
in another class, I inject those instance using javax.enterprise.inject.Instance<>
class SomeConfigurationClasss {
#Inject
#ServiceComponent()
Instance<Object> _restComponents;
#Override
public void iterate() throws Exception {
//iterate
for(Object obj : _restComponents){
somefuncion(obj);
}
//List.of(_restComponents)
//.flatMap(obj -> somefuncion(obj));
}
}
if I execute the "normal" iteration (for...) I get the Object (TruckService or CarService) given as parameter to the somefunction().
but if I use javaslang's List.of(...) I get the Instance itself. Which I think it's the expected behavior
Is there a possibility to use List.of on a Instance that can contain one or multiple bean (depending on the injection binding). (I already try to call iterator(), select() on the Instance)
Instance<T> extends Iterable<T> so you should use List#ofAll(Iterable)

Nested annotations method interception in Guice

I searched a lot but couldn't find anything much useful.
Problem:
I have created custom annotation like:
#MapExceptions(value = {
#MapException(sources = {IllegalArgumentException.class, RuntimeException.class}, destination = BadRequestException.class),
#MapException(sources = {RuntimeException.class}, destination = BadRequestException.class)
})
I am using Guice for DI.
Do I have to write two method interceptors? Actual work is getting done in #MapException
If yes, then how can I call #MapException interceptors invoke method from #MapExceptions interceptors invoke method? I dont want to duplicate code.
My #MapException interceptor looks like following
public class MapExceptionInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Exception actualException) {
Method method = invocation.getMethod();
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof MapException) {
MapException mapException = (MapException) annotation;
Class<? extends Throwable> destinationClass = mapException.destination();
Class<? extends Throwable>[] sourceClasses = mapException.sources();
for (Class sourceExceptionClass : sourceClasses) {
if (actualException.getClass().isInstance(sourceExceptionClass)) {
Constructor ctr = destinationClass.getConstructor(String.class);
throw (Throwable) ctr.newInstance(actualException.getMessage());
}
}
}
}
throw actualException;
}
}
}
I am using following Binding currently
bindInterceptor(Matchers.any(), Matchers.annotatedWith(MapException.class), new MapExceptionInterceptor());
Is this okay? Or I can improve?
Thank You !
So, the inner annotation is just a data bag.
To solve this I wrote interceptor for outer annotation (MapExceptions) which does all the work.

Pointcut matching methods with annotated parameters

I need to create an aspect with a pointcut matching a method if:
it is annoted with MyAnnotationForMethod
One of its parameters (can have many) is annotated with #MyAnnotationForParam (but can have other annotations as well).
The aspect class look like this
#Pointcut("execution(#MyAnnotationForMethod * *(..,#aspects.MyAnnotationForParam Object, ..)) && args(obj)")
void myPointcut(JoinPoint thisJoinPoint, Object obj) {
}
#Before("myPointcut(thisJoinPoint , obj)")
public void doStuffOnParam(JoinPoint thisJoinPoint, Object obj) {
LOGGER.info("doStuffOnParam :"+obj);
}
The annoted method
#MyAnnotationForMethod
public string theMethod(String a, #MyAnnotationForParam #OtherAnnotation Object obj, Object b){
LOGGER.info(a+obj+b);
}
With eclipse -> warnings : On the poincut :
Multiple markers at this line
- no match for this type name: MyAnnotationForMethod [Xlint:invalidAbsoluteTypeName]
- no match for this type name: aspects.MyAnnotationForParam On the before : advice defined in xxx.xxx.xxx.xxx.MyAspect has not been applied [Xlint:adviceDidNotMatch]
Using last aspectJ plugin from http://download.eclipse.org/tools/ajdt/35/update
With maven command line using aspectj 1.6.9
[WARNING] no match for this type name: MyAnnotationForMethod [Xlint:invalidAbsoluteTypeName]
[WARNING] no match for this type name: aspects.MyAnnotationForParam [Xlint:invalidAbsoluteTypeName]
[WARNING] advice defined in xxx.xxx.xxx.xxx.MyAspect has not been applied [Xlint:adviceDidNotMatch]
The annotations :
package com.xxx.xxx.annotation;
// standard imports stripped
#Documented
#Target( { FIELD, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface #MyAnnotationForParam {}
and
package com.xxx.xxx.annotation;
// standard imports stripped
#Target(METHOD)
#Retention(RUNTIME)
#Documented
public #interface MyAnnotationForMethod {}
And of course it doesn' work properly.
Can you tell me what is wrong ?
thx.
Updated:
OK, the best reference I could find is on this page: Annotations, Pointcuts and Advice.
You can match the method, however you won't be able to catch the parameter (just the method and the annotation). So what you will have to do is a combination of pointcut matching and reflection. Something like this:
#Pointcut(
"execution(#com.xxx.xxx.annotation.MyAnnotationForMethod * *(.., #com.xxx.xxx.annotation.MyAnnotationForParam (*), ..))")
public void annotatedMethod(){}
#Before("annotatedMethod()")
public void doStuffOnParam(final JoinPoint jp){
final Signature signature = jp.getSignature();
if(signature instanceof MethodSignature){
final MethodSignature ms = (MethodSignature) signature;
final Method method = ms.getMethod();
final String[] parameterNames = ms.getParameterNames();
final Class<?>[] parameterTypes = ms.getParameterTypes();
final Annotation[][] parameterAnnotations =
method.getParameterAnnotations();
for(int i = 0; i < parameterAnnotations.length; i++){
final Annotation[] annotations = parameterAnnotations[i];
final MyAnnotationForParam paramAnnotation =
getAnnotationByType(annotations, MyAnnotationForParam.class);
if(paramAnnotation != null){
this.processParameter(ms.toShortString(),
parameterNames[i],
parameterTypes[i],
paramAnnotation);
}
}
}
}
/**
* In an array of annotations, find the annotation of the specified type, if any.
* #return the annotation if available, or null
*/
#SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotationByType(final Annotation[] annotations,
final Class<T> clazz){
T result = null;
for(final Annotation annotation : annotations){
if(clazz.isAssignableFrom(annotation.getClass())){
result = (T) annotation;
break;
}
}
return result;
}
/**
* Do some processing based on what we found.
* #param signature method signature
* #param paramName parameter name
* #param paramType parameter type
* #param paramAnnotation annotation we found
*/
private void processParameter(final String signature,
final String paramName,
final Class<?> paramType,
final MyAnnotationForParam paramAnnotation){
System.out.println(MessageFormat.format(
"Found parameter ''{0}'' \n of type ''{1}'' \n with annotation ''{2}'' \n in method ''{3}''",
paramName,
paramType,
paramAnnotation,
signature));
}
Here is my test class for the above aspect:
public class TestClass{
#MyAnnotationForMethod
public void simpleTestMethod(#MyAnnotationForParam final String param1){
System.out.println("Method body (simple)");
};
#MyAnnotationForMethod
public void complexTestMethod(final String param1,
#MyAnnotationForParam final Float param2,
#MyAnnotationForParam final Boolean param3){
System.out.println("Method body (complex)");
};
public static void main(final String[] args){
System.out.println("Starting up");
final TestClass testObject = new TestClass();
testObject.simpleTestMethod("Hey");
testObject.complexTestMethod("Hey", 123.4f, false);
System.out.println("Finished");
}
}
and here is the output:
Starting up
Found parameter 'param1'
of type 'class java.lang.String'
with annotation '#com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.simpleTestMethod(..)'
Method body (simple)
Found parameter 'param2'
of type 'class java.lang.Float'
with annotation '#com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.complexTestMethod(..)'
Found parameter 'param3'
of type 'class java.lang.Boolean'
with annotation '#com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.complexTestMethod(..)'
Method body (complex)
Finished
Hint
You will probably want to cache a lot of this, there is no need to parse every parameter of every annotation in every execution. Keep a map of which parameter of which method carries the annotation and process only those parameters.
The ms.getParameterNames() call in the above solution doesnt seem to work when the method is implemented from an interface. I get back nulls.
However, if I enable CGLIB, then it works.
<aop:aspectj-autoproxy proxy-target-class="true"/>

Categories