How to use Custom Annotation in Java

Reading Time: 4 minutes

This article will take you on a journey of annotation. A brief description of annotation and their usage. A simple example of custom annotation and how to validate the custom annotation.

What is Annotation?

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

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.

When to use it?

Annotation is a very useful tool. It can be used for providing various capabilities.  For example,

  • If we want to add constraints on the attribute of the class. For example, the @Pattern annotation which except the regular expression. It will place a restriction on the field so that it can store a value that is supported by regular expression.
  • Providing the customs check to the field that can be globally be applied to different service.
  • Can be used to reduce the lines of code. For example, we can use the Lombok library to use @build annotation which provides the easy way to create the object of the class. It provides the getter and setter methods and many other things.

What is custom Annotation?

Custom annotation is a user-defined annotation to provide metadata. We can use it when we want to store the custom meta for class, methods, constructor or field.

How to use Custom Annotation?

I going to share a login application which simply adds validation on user class. User class has two field username and password. Where password field has the custom annotation.

We will begin with the making the custom annotation class for a password.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


package com.knoldus.user.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordValidation.class)
public @interface Password {
Class<?>[] groups() default {};
String message() default "the password must be 10 character long and must include the 1 numeric digit.";
Class<? extends Payload>[] payload() default {};
}
view raw

password

hosted with ❤ by GitHub

First, we make an interface name password as we have to apply the constraints on the password so name it as Password. It required to define the three methods those are group(),  message(), and the payload().

  • group: It allows the specification of validation groups, to which this constraint belongs.
  • message: This will return the text message in case constraints get violated.
  • payload: It is used by the client of the validation API to add the custom payload. It is not used by the API itself.

We have to the three annotations to the interface they are Target, Retention, and Constraint.

  • Target: It tells on what type of element custom annotation(Password) can be applied to. In our case, we have applied for methods and fields.
  • Retention:  It determines the retention policy that is what point annotation should be discarded. In our case, it is RUNTIME which determines it will be available to the JVM through the runtime.
  • Constraint: It specifies the constraint class that will perform the validation through the validatedBy property.

Moving on now we have to make the class that will perform the validation. Down is the code for that. Let us see what we need to do.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


package com.knoldus.user.annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordValidation implements ConstraintValidator<Password, String> {
private static final boolean NUMBER_OF_REQUIRED_DIGITS = true;
public static boolean validate(String password) {
if (password == null || password.length() == 0) {
return true;
} else if (password.length() < 10 || password.length() > 15) {
return false;
}
boolean numberOfDigits = false;
for (char p : password.toCharArray()) {
if (Character.isDigit(p)) {
numberOfDigits = true;
} else if (Character.isSpaceChar(p)) {
return false;
}
}
return (numberOfDigits == NUMBER_OF_REQUIRED_DIGITS);
}
public void initialize(Password gap) {
// No-op.
}
public boolean isValid(String password, ConstraintValidatorContext cac) {
// The password field should be annotated with @NotNull, so we return true because
// lack of a (null) password field is valid in certain validation scenarios.
return password == null || validate(password);
}
}

To make validation class that has the logic for the validation. A class needs to implement the ConstraintsValidator interface it takes to two java generics parameters. The first type is the annotation interface for which we have written this validation logic and the second type is the type of the value these validation is going to be applied. The initialize method allows creating all kind of initialization that is required for performing the validation check. The isValid method is responsible to perform the validation check: it must return true if validation passed and false otherwise. The first parameter of the isValid method represents the value to be validated and its type must be compatible with the type defined by the second generics parameter of the ConstraintValidator (i.e. String in our case). We have made the validate method that will be called in an isValid method. The validate method has the logic to validate the value in our case acceptable value for the password is that its length should lie between the 10 and 15,  and must contain at least one integer value.

Everything is ready we can now add the annotation on the variable on which we want constraints. Below we can see the example.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


package com.knoldus.user;
import com.knoldus.user.annotation.Password;
import lombok.Builder;
import lombok.Value;
import javax.validation.constraints.Pattern;
@Value
@Builder
public class User {
@Pattern(regexp ="^[a-zA-z]+$", message = "should be alpha numeric")
String username;
@Password
String password;
}
view raw

user

hosted with ❤ by GitHub

Till now we have learned to make custom annotation how to apply it. Now we going to see the validation in action.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


package com.knoldus.user;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Scanner;
import java.util.Set;
public class Login {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Please enter the username");
String username = sc.nextLine();
System.out.println("Please enter the password");
String password = sc.nextLine();
try {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
User newUser = User.builder()
.username(username)
.password(password)
.build();
Set<ConstraintViolation<User>> violations = validator.validate(newUser);
for (ConstraintViolation<User> violation : violations) {
if (!("".equals(violation.getMessage()))) {
throw new RuntimeException(violation.getMessage());
}
}
System.out.println("User created");
System.out.println(newUser.getUsername() + " Created\n");
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
view raw

login

hosted with ❤ by GitHub

Validator object, which is constructed using a ValidatorFactory in line number 23 and 24.

Now that we have a Validator, we can validate our bean by passing it to the validate method. Any violations of the constraints defined in the User object will be returned as a Set this can be seen in line number 29.

By iterating over the violations, we can get all the violation messages by using the getMessage method. In line number 23 if there is any message then a Runtime Exception is thrown with the violation message. There is a glitch with violation check it can through only first violation. This article gives an idea about how to check the custom validation.

There are few dependencies that you need to add before we execute this code.

 

Summary

An annotation is a powerful tool which conveniently applying behaviors to user-defined classes and methods that must otherwise be declared in an external source. Test of annotation can be done in a more efficient way.

Reference

knoldus-advt-sticker

Written by 

Priyanka Thakur is a Software Consultant having 6 months of experience currently working at Knoldus Software LLP. She has done MCA at BVICAM, GGSIPU. She has a graduation degree in BCA from JIMS, GGSIPU. She is familiar with programming language' s such as C, Java, and Scala. She also has interest in Angular and Docker and currently working on Logam. She is dedicated, focused and hardworking person. Her interests are in to face new programming challenges because she believes these challenges produce opportunity. She is keen to learn new technologies. In her leisure time, she prefers reading about mythology, watching movies.