Nov 8, 2018

How to write and apply custom annotation in Java

How to declare an Annotation

package ca.i88.example;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *
 * @author i88.ca
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)

public @interface I88CaCustomAnnotation {
    String param1();
}

@interface is the syntax used to declare an annotation. It is called an annotation interface.
The methods of the interface correspond to the elements of the annotation. param1() – This is the only element our annotation declaration consists of. It stores the name of the annotated field to display it in a message while processing.

See also:


How to write an Annotation Processor

An annotation processor tool parses the object it receives and takes programmed actions on finding the annotations it is processing in the object under scrutiny. Here is the annotation processor for our custom annotation:

package ca.i88.example;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author i88.ca
 */
public class I88CaCustomAnnotationProcessor {

    public static void processI88CaAnnotations(Object obj) {
        Class cl = obj.getClass();
        for (Field f : cl.getDeclaredFields()) {
            for (Annotation a : f.getAnnotations()) {
                if (a.annotationType() == I88CaCustomAnnotation.class) {
                    I88CaCustomAnnotation i88CaCustomAnnotation = (I88CaCustomAnnotation) a;
                    System.out.println("Processing the field : " + i88CaCustomAnnotation.param1());
                    // Setting the field to be accessible from our class
                    // if it is a private member of the class under processing
                    
                    // The setAccessible method will not work if you have
                    // Java SecurityManager configured and active.
                    f.setAccessible(true);
                    try {
                        if (f.get(obj).toString().contains("I88.CA")) {
                            System.out.println(f + " contains 'I88.CA'.");
                        } else {
                            System.out.println(f + " does NOT contain 'I88.CA'.");
                        }
                    } catch (IllegalArgumentException | IllegalAccessException ex) {
                        Logger.getLogger(I88CaCustomAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }
}

The above code uses Java Reflection API to process each of the Field in the received object parameter and process accordingly.

The following is the test of this custom annotation:
package ca.i88.example;

/**
 *
 * @author i88.ca
 */
public class AnnotationJavaApplication {

    @I88CaCustomAnnotation(param1 = "value1")
    private String value1;

    @I88CaCustomAnnotation(param1 = "value2")
    private String value2;

    public AnnotationJavaApplication() {
        this.value1 = "This has I88.CA";
        this.value2 = "this only has I88";
        // Calling the processor to process the annotations applied        
        // on this class object.    
        I88CaCustomAnnotationProcessor.processI88CaAnnotations(this);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        AnnotationJavaApplication annotationJavaApplication = new AnnotationJavaApplication();
    }

}

The output:

Processing the field : value1
private java.lang.String ca.i88.example.AnnotationJavaApplication.value1 contains 'I88.CA'.
Processing the field : value2
private java.lang.String ca.i88.example.AnnotationJavaApplication.value2 does NOT contain 'I88.CA'.