Java Built-in Annotations

  • @Override – When we want to override a method of Superclass, we should use this annotation to inform compiler that we are overriding a method. So when superclass method is removed or changed, compiler will show error message.
  • @Deprecated – when we want the compiler to know that a method is deprecated, we should use this annotation. Java recommends that in javadoc, we should provide information for why this method is deprecated and what is the alternative to use.
  • @SuppressWarnings – This is just to tell compiler to ignore specific warnings they produce, for example using raw types in generics. It’s retention policy is SOURCE and it gets discarded by compiler.

Meta annotations used in custom annotations

Target

@Target – indicates the kinds of program element to which an annotation type is applicable. Some possible values are TYPE, METHOD, CONSTRUCTOR, FIELD etc. If Target meta-annotation is not present, then annotation can be used on any program element. The java.lang.annotation.ElementType enum declares many constants to specify the type of element where annotation is to be applied such as TYPE, METHOD, FIELD etc. Let's see the constants of ElementType enum:

Element Types Where the annotation can be applied
TYPE class, interface or enumeration
FIELD fields
METHOD methods
CONSTRUCTOR constructors
LOCAL_VARIABLE local variables
ANNOTATION_TYPE annotation type
PARAMETER parameter

Example to specify annoation for a class

@Target(ElementType.TYPE)  
@interface MyAnnotation{  
    int value1();  
    String value2();  
}  
1
2
3
4
5

Example to specify annotation for a class, methods or fields

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})  
@interface MyAnnotation{  
    int value1();  
    String value2();  
}  
1
2
3
4
5

Retention

@Retention – indicates how long annotations with the annotated type are to be retained. It takes RetentionPolicy argument whose Possible values are SOURCE, CLASS and RUNTIME

RetentionPolicy Availability
RetentionPolicy.SOURCE refers to the source code, discarded during compilation. It will not be available in the compiled class.
RetentionPolicy.CLASS refers to the .class file, available to java compiler but not to JVM . It is included in the class file.
RetentionPolicy.RUNTIME refers to the runtime, available to java compiler and JVM .

Example to specify the RetentionPolicy

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)  
@interface MyAnnotation{  
   int value1();  
   String value2();  
}  
1
2
3
4
5
6

Inherited

  • @Inherited – indicates that an annotation type is automatically inherited. If user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class’s superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. By default, annotations are not inherited to subclasses. The @Inherited annotation marks the annotation to be inherited to subclasses.
@Inherited  
@interface ForEveryone { }//Now it will be available to subclass also  
  
@interface ForEveryone { }  
class Superclass{}  
  
class Subclass extends Superclass{}  
1
2
3
4
5
6
7

@Documented

  • @Documented – indicates that elements using this annotation should be documented by javadoc and similar tools. This type should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients. If a type declaration is annotated with Documented, its annotations become part of the public API of the annotated elements.

Type of annotations

Marker Annotation

An annotation that has no method, is called marker annotation. For example:

@interface MyAnnotation{}  
1

The @Override and @Deprecated are marker annotations.

Single-Value Annotation

An annotation that has one method, is called single-value annotation. For example:

@interface MyAnnotation{  
int value();  
}  
1
2
3

We can provide the default value also. For example:

@interface MyAnnotation{  
int value() default 0;  
}  
1
2
3

How to apply Single-Value Annotation

@MyAnnotation(value=10)
1

Multi-Value Annotation

An annotation that has more than one method, is called Multi-Value annotation. For example:

@interface MyAnnotation{  
   int value1();  
   String value2();  
   String value3();  
}  
1
2
3
4
5

We can provide the default value also. For example:

@interface MyAnnotation{  
    int value1() default 1;  
    String value2() default "";  
    String value3() default "xyz";  
}  
1
2
3
4
5

How to apply Multi-Value Annotation

@MyAnnotation(value1=10,value2="hello",value3="world")  

1
2

Custom Annotation Example1

import java.lang.annotation.*;  
import java.lang.reflect.*;  
  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@interface MyAnnotation{  
int value();  
}  
  
//Applying annotation  
class Hello{  
@MyAnnotation(value=10)  
public void sayHello(){System.out.println("hello annotation");}  
}  
  
//Accessing annotation  
class TestCustomAnnotation1{  
public static void main(String args[])throws Exception{  
  
Hello h=new Hello();  
Method m=h.getClass().getMethod("sayHello");  
  
MyAnnotation manno=m.getAnnotation(MyAnnotation.class);  
System.out.println("value is: "+manno.value());  
}}  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Example2

annoation define

@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo {
    String author() default "Freshal";
    String date();
    int revision() default 1;
    String comments();
}
1
2
3
4
5
6
7
8
9
10

class using annotation

public class MyObject {
    @Override
    @MethodInfo(author = "freshal", comments = "Main method", date = "Nov 17 2012", revision = 1)
    public String toString() {
        return "Overriden toString method";
    }

    @Deprecated
    @MethodInfo(comments = "deprecated method", date = "Nov 17 2012")
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }

    @SuppressWarnings({"unchecked", "deprecation"})
    @MethodInfo(author = "freshal", comments = "Main method", date = "Nov 17 2012", revision = 10)
    public static void genericsTest() throws FileNotFoundException {
        List l = new ArrayList();
        l.add("abc");
        oldMethod();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public class Main {

    public static void main(String[] args) {

        Class<MyObject> obj = MyObject.class;
        try {
            for (Method method : obj.getDeclaredMethods()) {
                // checks if MethodInfo annotation is present for the method
                if (method
                        .isAnnotationPresent(com.freshal.example.annotation.MethodInfo.class)) {
                    try {
                        // iterates all the annotations available in the method
                        for (Annotation anno : method.getDeclaredAnnotations()) {
                            System.out.println("Annotation in Method "
                                    + method + ":" + anno);
                        }
                        MethodInfo methodAnno = method
                                .getAnnotation(MethodInfo.class);
                        if (methodAnno.revision() == 1) {
                            System.out.println("Method with revision no 1 = "
                                    + method);
                        }

                    } catch (Throwable ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

Example3:Spring Custom Annotation

Annotation Class


@Retention(RetentionPolicy.RUNTIME) //Annotation will work at RUNTIME
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) //Annotation can be applied either on METHOD or on FIELD
@Constraint(validatedBy = CustomValidator.class) //CustomValidator class will validate the values
public @interface SupportedValues 
{
	String message() default "Values are not supported";
	
	String[] values();
	
	Class<?>[] groups() default {}; //Required by Constraint
	
	Class<? extends Payload>[] payload() default {}; //Required by Constraint
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Validator Class

public class CustomValidator implements ConstraintValidator<SupportedValues, String>{

	public String message;
	public String[] values;
	
	@Override
	public void initialize(SupportedValues supportedValues) 
	{
		this.message = supportedValues.message();
		this.values = supportedValues.values();
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext arg1) 
	{
		List<String> lstValues = Arrays.asList(values);
		
		return value != null && !value.isEmpty() && lstValues.contains(value);
	}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Class using Annotation

@Component
public class ConfigProp {

	@SupportedValues(message = "Invalid values found for dbType", values = {"ORACLE", "MYSQL", "SQL"})
	private String dbType;

	public String getDbType() {
		return dbType;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Spring Boot class for testing

@SpringBootApplication
public class SpringBootMain implements CommandLineRunner
{
	public static void main(String[] args) {
		SpringApplication.run(SpringBootMain.class, args);
	}

	@Override
	public void run(String... args) throws Exception 
	{

		String dbType = "POSTGRES";
		
		ConfigProp property = new ConfigProp();
		property.setDbType(dbType);//setting unsupported values here
		
		//Inbuilt class that is used to validate data
		ValidatorFactory validator = Validation.buildDefaultValidatorFactory();
		Set<ConstraintViolation<ConfigProp>> validationErrors = validator.getValidator().validate(property);
		if(!validationErrors.isEmpty()) //If there are some errors then print those
		{
			for(ConstraintViolation<ConfigProp> invalidObj : validationErrors)
			{
				System.out.println(invalidObj.getMessage());
			}
		}
		
	}
	
	
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Further read:Spring Annotation with AOP

https://moelholm.com/2016/10/15/spring-4-3-custom-annotations/