Java annotation is a useful skill, this is an article that will talk about what is Java Annotation? And how to use it?

What is Java Annotation?

Let’s see a code segment as the following:

// this is a component:
@Resource("hello")
public class Hello {
    @Inject
    int n;

    @PostConstruct
    public void hello(@Param String name) {
        System.out.println(name);
    }

    @Override
    public String toString() {
        return "Hello";
    }
}

Do you notice that these start with @, such as @Resource, @Override, it’s annotation. Java annotation is a special kind of “comment” placed before the classes, methods, fields, and parameters of the Java source code.

Java annotation likes a kind of “metadata” used for annotation.

Three types of annotation

Java annotation can be divided into three types.

1.Annotation for compiler

The first type of annotations is used by the compiler, for example:

  • @Override: Let the compiler check if the method implements the override correctly;
  • @SuppressWarnings: Tells the compiler to ignore warnings generated by this code.

These annotations are not compiled into .classfiles, they are thrown away by the compiler after compilation.

2.Annotation for tools to process .class files

The second type is the annotations used by the tool to process the .class file. For example, some tools will dynamically modify the class when loading the class to achieve some special functions. This kind of annotation will be compiled into the .classfile, but will not exist in memory after loading. 

This type of annotation is only used by some low-level libraries, and generally, we don’t have to deal with it ourselves.

Runtime annotation

The third type is annotations that can be read during program runtime. They always exist in the JVM after loading. This is also the most commonly used annotation. 

For example, a configured @PostConstructmethod is automatically called after the constructor is called (this is a function implemented by Java code to read the annotation, and the JVM does not recognize the annotation).

How to define annotation?

The Java language uses @interfacesyntax to define annotation ( Annotation), and its format is as follows:

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

Annotated parameters are similar to the no-parameter method, and you can use defaultset a default value (recommended).  And the most commonly used parameter should be named value.

Firstly, let’s see the meta annotation provided by the Java standard library.

@Target

The most commonly used Java meta annotation is @Target. Use @Targetto define where the Annotationsource code can be applied:

  • Class or interface ElementType.TYPE:;
  • Field:ElementType.FIELD ;
  • Method : ElementType.METHOD;
  • Construction method ElementType.CONSTRUCTOR;
  • Method ElementType.PARAMETER.

For example, to define an annotation that @Reportcan be used on methods, we must add one @Target(ElementType.METHOD):

@Target(ElementType.METHOD)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

Defining annotation @Reportcan be used on methods or fields. You can turn @Targetannotation parameters into arrays { ElementType.METHOD, ElementType.FIELD }:

@Target({
    ElementType.METHOD,
    ElementType.FIELD
})
public @interface Report {
    ...
}

@Retention

Another important meta annotation @Retentiondefines the Annotationlife cycle:

  • Only in compile time: RetentionPolicy.SOURCE;
  • Only class file: RetentionPolicy.CLASS;
  • Runtime:RetentionPolicy.RUNTIME .

If @Retentionnot present, the Annotationdefault is CLASS

Because we usually customize Annotationis RUNTIMEannotation, so, be sure to add @Retention(RetentionPolicy.RUNTIME)meta annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Repeatable

Use @Repeatable meta annotation to define Annotationif it is repeatable. This annotation is not particularly widespread.

@Repeatable(Reports.class)
@Target(ElementType.TYPE)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Target(ElementType.TYPE)
public @interface Reports {
    Report[] value();
}

After setting @Repeatablemodification, you can add multiple @Reportannotations at a type declaration :

@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Hello {
}

@Inherited

Use @Inherited to define whether a subclass can inherit the definition of a parent Annotation class. 

@Inheritedonly valid for @Target(ElementType.TYPE)type annotation, and only for classinheritance, not for interface inheritance:

@Inherited
@Target(ElementType.TYPE)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

In use, if a class is used @Report:

@Report(type=1)
public class Person {
}

Then its subclass also defines the annotation by default:

public class Student extends Person {
}

Define annotation

The first step is using @interface to define an annotation:

public @interface Report {
}

The second step is to add parameters and default values:

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

The most commonly used parameters are defined value(), and it is recommended that all parameters be set to the default values ​​as much as possible.

The third step is to configure annotations with meta annotations:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

Which must be set @Targetand @Retention, the @Retentiongenerally setted RUNTIME, because our custom annotation usually requires reading at runtime. 

In general, there is no need to write a @Inheritedsum @Repeatable.

How to use a custom annotation?

After defining a Java annotation, then how to use the custom annotation?

It’s decided by yourself.

For example, let’s look at an @Rangeannotation, we want to use it to define the rules of a String field: @Rangethe parameter definition that the field length should satisfy:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
    int min() default 0;
    int max() default 255;
}

In a JavaBean, we can use the annotation like that:

public class Person {
    @Range(min=1, max=20)
    public String name;

    @Range(max=10)
    public String city;
}

However, annotations are defined and have no effect on the logic of the program itself. We have to write the code by ourselves to use annotations. 

Here, we write an Personinstance check method that can check if the String field length of the Person instance meets the@Rangedefinition:

void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
    
    for (Field field : person.getClass().getFields()) {
        Range range = field.getAnnotation(Range.class);
        if (range != null) {
            // get the value of the field
            Object value = field.get(person);
            if (value instanceof String) {
                String s = (String) value;
                // verify if the value satisfy the rules of length
                if (s.length() < range.min() || s.length() > range.max()) {
                    throw new IllegalArgumentException("Invalid field: " 
                              + field.getName());
                }
            }
        }
    }
}

By this way, we can complete the inspection of the instance of Person through @Rangeannotation and check()method. 

Note that the inspection logic is completely written by ourselves, and the JVM does not automatically add any extra logic to the annotation.