Table of Contents
Annotation and Reflection
- Annotations have a number of uses, among them:
- Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
- Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
- Runtime processing — Some annotations are available to be examined at runtime.
- Annotation type declarations are similar to normal interface declarations.
- An at-sign (@) precedes the interface keyword – Sample : public @interface ClassInfo
- The annotations are not method calls and will not, by themselves, do anything. Rather any tool like JPA need to extract the annotations at runtime and need to do execute the designed action like: Generating an object-relational mapping.
- Following JAVA language constructs can be annotated: Class, Constructor, Field, Method, and Package
- The Java compiler conditionally stores annotation metadata in the class files if the annotation has a RetentionPolicy of CLASS or RUNTIME. Later, the JVM or other programs can look for the metadata to determine how to interact with the program elements or change their behavior [ via Reflection API ]
- Annotation can also be used to provide some info about who change a component [ Java Class , Java Methode ]
Custom Annotations Details for Runtime processing with Reflection
ClassInfo - @Target(value = ElementType.TYPE) - Provide some Info who has changed a certain specific JAVA source - Use reflection code below to display the Anotation Info if(annotation instanceof ClassInfo) { ClassInfo myAnnotation = (ClassInfo) annotation; System.out.println(" -> autor : " + myAnnotation.author()); System.out.println(" -> date : " + myAnnotation.date()); System.out.println(" -> comment : " + myAnnotation.comments()); } CanRun - @Target(value = ElementType.METHOD) - Indicate that we can/should run a certain JAVA methode via reflection - Run that specific methode by using reflection code: method.invoke(runner); CanChange - @Target(value = ElementType.FIELD) - Indicate that we can/should modify a certain JAVA methode via reflection - Change a int field by using reflection code: f.setInt(runner,k); CanConstruct - @Target(value = ElementType.CONSTRUCTOR) - Indicates that we can/should run a certain JAVA Constructor via reflection - Construct a new AnnotationRunner instance and printout the int field id1 by using reflection code: ctor.setAccessible(true); AnnotationRunner r = (AnnotationRunner)ctor.newInstance(); Field f = r.getClass().getDeclaredField("id1"); f.setAccessible(true); Note all of the above Anotations are used during Runtime and thus @Retention(value = RetentionPolicy.RUNTIME) is mandatory
Java source: ClassInfo.java
package utils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.TYPE) @Retention(value = RetentionPolicy.RUNTIME) public @interface ClassInfo { String author() default "Helmut"; String date(); String comments(); }
Java source: CanChange.java
package utils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.FIELD) @Retention(value = RetentionPolicy.RUNTIME) public @interface CanChange { }
Java source: CanRun.java
package utils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) public @interface CanRun { }
Java source: CanConstruct.java
package utils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.CONSTRUCTOR) @Retention(value = RetentionPolicy.RUNTIME) public @interface CanConstruct { }
Java source – The helper class: ReflectionUtils.java
package utils; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Die Klasse <code>ReflectionUtils</code> ist eine Utility-Klasse, die * verschiedene Hilfsmethoden zur vereinfachten Handhabung von Reflection * bereitstellt. * * @author Michael Inden * * Copyright 2011 by Michael Inden */ public final class ReflectionUtils { public static String modifierToString(final int modifier) { String modifiers = ""; if (Modifier.isPublic(modifier)) { modifiers += "public "; } if (Modifier.isProtected(modifier)) { modifiers += "protected "; } if (Modifier.isPrivate(modifier)) { modifiers += "private "; } if (Modifier.isStatic(modifier)) { modifiers += "static "; } if (Modifier.isAbstract(modifier)) { modifiers += "abstract "; } if (Modifier.isFinal(modifier)) { modifiers += "final "; } if (Modifier.isVolatile(modifier)) { modifiers += "volatile "; } if (Modifier.isSynchronized(modifier)) { modifiers += "synchronized "; } return modifiers; } public static Field findField(final Class<?> clazz, final String fieldName) { // Abbruch der Rekursion if (clazz == null) { return null; } try { return clazz.getDeclaredField(fieldName); } catch (final NoSuchFieldException ex) { // rekursive Suche in Superklasse return findField(clazz.getSuperclass(), fieldName); } } public static Method findMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes) { // Abbruch der Rekursion if (clazz == null) { return null; } try { return clazz.getDeclaredMethod(methodName, parameterTypes); } catch (final NoSuchMethodException ex) { // rekursive Suche in Superklasse return findMethod(clazz.getSuperclass(), methodName, parameterTypes); } } public static Method[] getAllMethods(final Class<?> clazz) { final List<Method> methods = new ArrayList<Method>(); methods.addAll(Arrays.asList(clazz.getDeclaredMethods())); /* if (clazz.getSuperclass() != null) { // rekursive Suche in Superklasse methods.addAll(Arrays.asList(getAllMethods(clazz.getSuperclass()))); } */ return methods.toArray(new Method[0]); } public static Field[] getAllFields(final Class<?> clazz) { final List<Field> fields = new ArrayList<Field>(); // Field[] fields = cls.getDeclaredFields(); fields.addAll(Arrays.asList(clazz.getDeclaredFields())); return fields.toArray(new Field[0]); } public static Constructor[] getAllConstructors(final Class<?> clazz) { final List<Constructor> constructors = new ArrayList<Constructor>(); // Field[] fields = cls.getDeclaredFields(); constructors.addAll(Arrays.asList(clazz.getConstructors())); return constructors.toArray(new Constructor[0]); } public static void printCtorInfos(final Constructor<?> ctor) { System.out.println(modifierToString(ctor.getModifiers()) + " " + ctor.getName() + buildParameterTypeString(ctor.getParameterTypes())); printAnnotations(ctor.getAnnotations()); } public static void printMethodInfos(final Method method) { System.out.println(modifierToString(method.getModifiers()) + method.getReturnType() + " " + method.getName() + buildParameterTypeString(method.getParameterTypes())); printAnnotations(method.getAnnotations()); } public static void printFieldInfos(final Field field) { System.out.println(ReflectionUtils.modifierToString(field.getModifiers()) + field.getType() + " " + field.getName()); printAnnotations(field.getAnnotations()); } public static String buildParameterTypeString(final Class<?>[] parameterTypes) { if (parameterTypes.length > 0) { return "(" + Arrays.toString(parameterTypes) + ")"; } return "()"; } private static void printAnnotations(final Annotation[] annotations) { if (annotations.length > 0) { System.out.println("Annotations: " + Arrays.toString(annotations)); } } public static void printClassInfo(final Class<?> clazz ) { // System.out.println("Canonical Class Name: " + clazz.getCanonicalName() ); System.out.println("Class Name : " + clazz.getName() ); System.out.println("Superclass Name : " + clazz.getSuperclass() ); System.out.println("Interfaces : " + Arrays.toString(clazz.getInterfaces())); // Class c = runner.getClass(); Annotation[] annotations = clazz.getAnnotations(); for(Annotation annotation : annotations) { if(annotation instanceof ClassInfo) { ClassInfo myAnnotation = (ClassInfo) annotation; System.out.println(" -> autor : " + myAnnotation.author()); System.out.println(" -> date : " + myAnnotation.date()); System.out.println(" -> comment : " + myAnnotation.comments()); } } } private ReflectionUtils() { } }
Java source: AnnotationRunner.java
package AnnotationTest; import utils.CanChange; import utils.CanRun; import utils.CanConstruct; import utils.ClassInfo; @ClassInfo( author="Helmut Hutzer", date="8-Feb-2014", comments="Intial Class Creation for Testing annotations" ) public class AnnotationRunner { @CanChange public int id1 = 1; public int id2= 2; @CanConstruct public AnnotationRunner() { } public AnnotationRunner(int v_id1, int v_id2) { id1 = v_id1; id2 = v_id2; } public void method1() { System.out.println("Hello from method1 : " + id1); } @CanRun public void method2() { System.out.println("Hello from method2 !"); } public void set_id1(int v_id) { id1 = v_id; } public void set_id2(int v_id) { id2 = v_id; } }
Java source: MyTest.java
package AnnotationTest; import java.lang.annotation.Annotation; import utils.CanChange; import utils.CanRun; import utils.CanConstruct; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class MyTest { public static void main(String[] args) { AnnotationRunner runner = new AnnotationRunner(); System.out.println("\n-->Inspect Class:"); utils.ReflectionUtils.printClassInfo( runner.getClass()); System.out.println("\n-->Inspect Class:"); utils.ReflectionUtils.printClassInfo(CanChange.class); System.out.println("\n--> Exploring Constructors "); Constructor[] constructors = utils.ReflectionUtils.getAllConstructors(runner.getClass()); Constructor ctor =null; for ( Constructor constructor : constructors) { utils.ReflectionUtils.printCtorInfos(constructor); // Find the constructor without any paramter if (constructor.getGenericParameterTypes().length == 0) ctor = constructor; } // See ; http://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html if ( ctor != null ) { System.out.println("--> Found a constructor without parameters "); Annotation annos = ctor.getAnnotation(CanConstruct.class); if (annos != null) { try { System.out.println("--> Found Annotation CanConstruct "); utils.ReflectionUtils.printCtorInfos(ctor); ctor.setAccessible(true); AnnotationRunner r = (AnnotationRunner)ctor.newInstance(); Field f = r.getClass().getDeclaredField("id1"); f.setAccessible(true); System.out.println("--> Created new instance of class AnnotationRunner"); System.out.println("--> print Field id1 via reflecion : "+ f.get(r)); } catch ( InstantiationException | IllegalAccessException | NoSuchFieldException | InvocationTargetException ex ) { ex.printStackTrace(); } } } System.out.println("\n --> Exploring method annotations "); // utils.ReflectionUtils.getAllMethods scans your superclass too ! Method[] methods = utils.ReflectionUtils.getAllMethods(runner.getClass()); for (Method method : methods) { utils.ReflectionUtils.printMethodInfos(method); Annotation annos = method.getAnnotation(CanRun.class); if (annos != null) { System.out.println("--> Found CanRun.class Annotation for method: " + method.getName() ); try { System.out.println("--> Invoking this method via reflection "); method.invoke(runner); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println("--> Found NO Annotation for method: " + method.getName() ); } } // Field[] fields = runner.getClass().getFields(); System.out.println("\n --> Exploring Attributes:"); Field[] fields = utils.ReflectionUtils.getAllFields(runner.getClass()); for (Field f : fields) { utils.ReflectionUtils.printFieldInfos(f); Annotation annos = f.getAnnotation(CanChange.class); if (annos != null) { System.out.println("--> Found Annotation for field: " + f.getName() + " " + f.getType() ); try { if ( f.getType().equals(int.class) ) { int k = 99 ; System.out.println("--> Current value for id1 : " + runner.id1 ); f.setInt(runner,k); System.out.println("--> Changing int value for attr " +f.getName() + " via reflection - New value: " + runner.id1 ); } } catch (IllegalAccessException ex) { ex.printStackTrace(); } } } } }
Program Output running MyTest.java
-->Inspect Class: Canonical Class Name: AnnotationTest.AnnotationRunner Class Name : AnnotationTest.AnnotationRunner Superclass Name : class java.lang.Object Interfaces : [] -> autor : Helmut Hutzer -> date : 8-Feb-2014 -> comment : Intial Class Creation for Testing annotations -->Inspect Class: Canonical Class Name: utils.CanChange Class Name : utils.CanChange Superclass Name : null Interfaces : [interface java.lang.annotation.Annotation] --> Exploring Constructors public AnnotationTest.AnnotationRunner([int, int]) public AnnotationTest.AnnotationRunner() Annotations: [@utils.CanConstruct()] --> Found a constructor without parameters --> Found Annotation CanConstruct public AnnotationTest.AnnotationRunner() Annotations: [@utils.CanConstruct()] --> Created new instance of class AnnotationRunner --> print Field id1 via reflecion : 1 --> Exploring method annotations public void method1() --> Found NO Annotation for method: method1 public void method2() Annotations: [@utils.CanRun()] --> Found CanRun.class Annotation for method: method2 --> Invoking this method via reflection Hello from method2 ! public void set_id1([int]) --> Found NO Annotation for method: set_id1 public void set_id2([int]) --> Found NO Annotation for method: set_id2 --> Exploring Attributes: public int id1 Annotations: [@utils.CanChange()] --> Found Annotation for field: id1 int --> Current value for id1 : 1 --> Changing int value for attr id1 via reflection - New value: 99 public int id2
Reference
- http://www.vogella.com/tutorials/JavaAnnotations/article.html
- http://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
- Michael Inden : Der Weg zum Java-Profi