Hello,
I would present you an example concerning the annotation in JAVA: validation POJO with annotation.
Brief presentation
Introduced with Java SE 5, the annotations are more increasingly used in the development of applications. Annotations provide informations about a class and they have no direct effect on the annotated code. Annotations can be preserved at runtime (RetentionPolicy.RUNTIME) or are only available at development time, during the compile (RetentionPolicy.SOURCE).
There are standard annotations and Java allows the definition of custom annotations.
For example, the following standard annotations are often used in Java:
@Override:
This annotation added to a method, notifies the Java compiler to check if the annotated method really overrides a method of an interface or the extended class.
02 | public String toString(){ |
03 | return this .getClass().getName() |
07 | + ", numberOfRead=" +numberOfRead |
08 | + ", lastReadDateTime=" +lastReadDateTime |
09 | + ", authors=" +authors |
@Deprecated
This annotation added to a field, method or constructor, indicates that the annotated element should not be used anymore. It is possible to add this annotation to a class, however, this doesn’t deprecate automatically all its fields and methods.
@Target
This annotation specifies where it is legal to use an annotation type. The possible values are constants from the
java.lang.annotation.ElementType enumeration which provides a simple classification of the declared elements in a Java program.
There are the following constants:
- ANNOTATION_TYPE – Annotation type declaration – used for other annotations.
2 | public @interface AnotherAnnotation {..} |
- CONSTRUCTOR – Constructor declaration
1 | public class MyAnnotatedClass { |
3 | public MyAnnotatedClass() {..} |
- FIELD – Field declaration (includes enum constants)
2 | private String myAnnotatedField; |
- LOCAL_VARIABLE – Local variable declaration – It can’t be read at runtime, so it is used only for compile-time things, like the @SuppressWarnings annotation.
1 | public void someMethod() { |
2 | @MyAnnotation int myLocalVariable = 0 ; |
- METHOD – Method declaration
2 | public void myAnnotatedMethod() {..} |
- PACKAGE – Package declaration
2 | package com.ho.myannotatedpackage; |
- PARAMETER – Parameter declaration
1 | public void myMethod( @MyAnnotation param) {..} |
- TYPE – Class, interface (including annotation type), or enum declaration
2 | public class MyAnnotatedClass {..} |
It is possible to specify multiple ElementTypes for a given annotation:
1 | @Target ({ElementType.CONSTRUCTOR, ElementType.METHOD}) |
Example : validation with annotations
When an annotation is implemented, it is necessary to tell two things to the compiler:
- where it can be applied (that’s the @Target annotation);
- what its retention policy is (aptly named @RetentionPolicy; some annotations are kept only at the source level, others at runtime).
Below, the official description of annotations retention policy:
1 | + RetentionPolicy.SOURCE: Discard during the compile. These annotations don't make any sense after the compile has completed, so they aren't written to the bytecode. |
2 | Example: @Override, @SuppressWarnings |
4 | + RetentionPolicy.CLASS: Discard during class load. Useful when doing bytecode-level post-processing. Somewhat surprisingly, this is the default. |
6 | +RetentionPolicy.RUNTIME: Do not discard. The annotation should be available for reflection at runtime. Example: @Deprecated |
In our example, we will use the annotations with the JDK 1.6 for validate the POJO values on two criterias ans set its name:
- an annotation Mandatory to specify if a field is mandatory or not,
- an annotation MaxLength to specify the max length of a String field,
- an annotation FiledName to set the field’s name,
STEP 1
Create a new project named test_annot_valid with the following librairies in test_annot_valid\lib folder:
- commons-beanutils-1.9.0.jar,
- commons-lang-2.6.jar,
- commons-logging-1.1.3.jar,
For information, our project structure will be:

STEP 2
We will create the definition of new annotations by creating 3 new classes in the package “com.ho.annotations.common.annotations”:
FieldName:
01 | package com.ho.annotations.common.annotations; |
03 | import java.lang.annotation.ElementType; |
04 | import java.lang.annotation.Retention; |
05 | import java.lang.annotation.RetentionPolicy; |
06 | import java.lang.annotation.Target; |
08 | @Retention (RetentionPolicy.RUNTIME) |
09 | @Target (ElementType.FIELD) |
10 | public @interface FieldName { |
14 | public enum EnumFieldName{ |
Mandatory:
01 | package com.ho.annotations.common.annotations; |
03 | import java.lang.annotation.ElementType; |
04 | import java.lang.annotation.Retention; |
05 | import java.lang.annotation.RetentionPolicy; |
06 | import java.lang.annotation.Target; |
10 | * Marks the field as Mandatory : a null value is illegal. |
14 | @Retention (RetentionPolicy.RUNTIME) |
15 | @Target (ElementType.FIELD) |
16 | public @interface Mandatory { |
Maxlength:
01 | package com.ho.annotations.common.annotations; |
03 | import java.lang.annotation.ElementType; |
04 | import java.lang.annotation.Retention; |
05 | import java.lang.annotation.RetentionPolicy; |
06 | import java.lang.annotation.Target; |
09 | * Specify the maximum length of the annotated field. |
13 | @Retention (RetentionPolicy.RUNTIME) |
14 | @Target (ElementType.FIELD) |
15 | public @interface MaxLength { |
STEP 3
Then, it is necessary to create POJO on whom the previous annotations must be applied. The class AbstractEntity will contain a list of errors corresponding to validation errors:
01 | package com.ho.annotations.data.entity; |
03 | import java.util.ArrayList; |
06 | public abstract class AbstractEntity { |
09 | protected List<ErrorEntity> errors = new ArrayList<ErrorEntity>(); |
12 | public void addError(ErrorEntity error){ |
13 | this .errors.add(error); |
16 | public void addErrors(List<ErrorEntity> errors){ |
17 | this .errors.addAll(errors); |
20 | public boolean hasError(){ |
21 | return errors!= null && errors.size()> 0 ; |
24 | public List<ErrorEntity> getErrors(){ |
25 | List<ErrorEntity> result = new ArrayList<ErrorEntity>(); |
26 | for (ErrorEntity error : errors){ |
And an error entity ErrorEntity class representing one error that occured when validating the object:
01 | package com.ho.annotations.data.entity; |
03 | import com.ho.annotations.common.annotations.FieldName; |
06 | * Entity class representing one error that occured when validating the object |
10 | public class ErrorEntity extends AbstractEntity{ |
12 | public static enum ErrorLevel{ |
21 | private FieldName.EnumFieldName enumFieldName; |
23 | private Integer entityId; |
24 | private ErrorLevel errorLevel; |
27 | public ErrorEntity(){} |
29 | public ErrorEntity(FieldName.EnumFieldName enumFieldName, String error){ |
30 | this .enumFieldName = enumFieldName; |
32 | this .errorLevel = ErrorLevel.ERROR; |
35 | public ErrorEntity(FieldName.EnumFieldName enumFieldName, String error, ErrorLevel errorLevel){ |
36 | this .enumFieldName = enumFieldName; |
38 | this .errorLevel = errorLevel; |
43 | public String toString(){ |
44 | StringBuilder errorBuilder = new StringBuilder(); |
45 | errorBuilder.append(getEnumFieldName()); |
46 | errorBuilder.append( " " ); |
47 | errorBuilder.append(getError()); |
48 | return errorBuilder.toString(); |
53 | public Integer getId() { |
57 | public void setId(Integer id) { |
61 | public FieldName.EnumFieldName getEnumFieldName() { |
65 | public void setEnumFieldName(FieldName.EnumFieldName enumFieldName) { |
66 | this .enumFieldName = enumFieldName; |
Finally, the entity class BookEntity will be validated:
01 | package com.ho.annotations.data.entity; |
03 | import java.util.Calendar; |
05 | import com.ho.annotations.common.annotations.FieldName; |
06 | import com.ho.annotations.common.annotations.Mandatory; |
07 | import com.ho.annotations.common.annotations.MaxLength; |
10 | * Entity mapping the table BOOK |
14 | public class BookEntity extends AbstractEntity{ |
19 | @FieldName (name=FieldName.EnumFieldName.ID) |
24 | @MaxLength (maxLength = 10 ) |
25 | @FieldName (name=FieldName.EnumFieldName.NAME) |
30 | @FieldName (name=FieldName.EnumFieldName.PRICE) |
34 | @FieldName (name=FieldName.EnumFieldName.NB_OF_READ) |
35 | private Integer numberOfRead; |
38 | @FieldName (name=FieldName.EnumFieldName.LAST_READ_DATETIME) |
39 | private Calendar lastReadDateTime; |
42 | @FieldName (name=FieldName.EnumFieldName.AUTHORS) |
43 | private String[] authors; |
50 | public String toString(){ |
51 | return this .getClass().getName() |
55 | + ", numberOfRead=" +numberOfRead |
56 | + ", lastReadDateTime=" +lastReadDateTime |
57 | + ", authors=" +authors |
62 | public Integer getId() { |
66 | public void setId(Integer id) { |
So, the book entity has the following fields:
- id containing the technical ID : mandatory field named ID,
- name corresponding to the book’s name : mandatory field named NAME with a max length of 10 characters,
- price corresponding to the book’s price : mandatory field named PRICE,
- numberOfRead corresponding to the number of read of this book : no mandatory field named NB_OF_READ,
- lastReadDateTime corresponding to the last read datetime of this book : no mandatory field named LAST_READ_DATETIME,
- authors containing the book’s authors : no mandatory field named AUTHORS,
STEP 4
In this step, we create an utility class ValidatorUtil to handle the validation of entities:
01 | package com.ho.annotations.validator; |
03 | import java.lang.reflect.Field; |
04 | import java.util.ArrayList; |
07 | import org.apache.commons.beanutils.PropertyUtils; |
09 | import com.ho.annotations.common.annotations.FieldName; |
10 | import com.ho.annotations.common.annotations.Mandatory; |
11 | import com.ho.annotations.common.annotations.MaxLength; |
12 | import com.ho.annotations.data.entity.AbstractEntity; |
13 | import com.ho.annotations.data.entity.ErrorEntity; |
16 | * This class handles the validation of the entity. |
19 | public final class ValidatorUtil { |
22 | private ValidatorUtil(){} |
26 | * Validation of the given entity annotations |
30 | public static void validateEntity(AbstractEntity myEntity) throws Exception{ |
31 | myEntity.addErrors(validateAnnotatedFields(myEntity)); |
37 | * Validates all the annotated field of the given entities. |
38 | * Supported annotation are {@see Mandatory} {@see MaxLength} |
43 | private static List<ErrorEntity> validateAnnotatedFields(AbstractEntity myEntity) throws Exception{ |
44 | List<ErrorEntity> errors = new ArrayList<ErrorEntity>(); |
46 | for (Field field : myEntity.getClass().getDeclaredFields()){ |
48 | final Mandatory mandatory = field.getAnnotation(Mandatory. class ); |
49 | if (mandatory != null ){ |
50 | final Object fieldValue = PropertyUtils.getProperty(myEntity, field.getName()); |
51 | if (fieldValue == null ){ |
52 | final FieldName fieldName = field.getAnnotation(FieldName. class ); |
53 | errors.add( new ErrorEntity(fieldName.name(), "Mandatory." )); |
58 | final MaxLength maxLength = field.getAnnotation(MaxLength. class ); |
59 | if (maxLength != null ){ |
60 | final Object fieldValue = PropertyUtils.getProperty(myEntity, field.getName()); |
61 | if (fieldValue instanceof String ){ |
62 | if (maxLength.maxLength() < ((String)fieldValue).length()){ |
63 | final FieldName fieldName = field.getAnnotation(FieldName. class ); |
64 | errors.add( new ErrorEntity(fieldName.name(), "Value is too large. Max length is: " +maxLength.maxLength())); |
The important point of this class is the part concerning the checking of annotations on entity object:
2 | final Mandatory mandatory = field.getAnnotation(Mandatory. class ); |
4 | final Object fieldValue = PropertyUtils.getProperty(myEntity, field.getName()); |
5 | if (fieldValue == null ){ |
6 | final FieldName fieldName = field.getAnnotation(FieldName. class ); |
7 | errors.add( new ErrorEntity(fieldName.name(), "Mandatory." )); |
02 | final MaxLength maxLength = field.getAnnotation(MaxLength. class ); |
04 | final Object fieldValue = PropertyUtils.getProperty(myEntity, field.getName()); |
05 | if (fieldValue instanceof String ){ |
06 | if (maxLength.maxLength() < ((String)fieldValue).length()){ |
07 | final FieldName fieldName = field.getAnnotation(FieldName. class ); |
08 | errors.add( new ErrorEntity(fieldName.name(), "Value is too large. Max length is: " +maxLength.maxLength())); |
STEP 5
Finally, we are creating a main method TestValidator to check our valiation based on annotations:
01 | package com.ho.annotations.validator.test; |
03 | import java.util.GregorianCalendar; |
04 | import java.util.Iterator; |
06 | import com.ho.annotations.data.entity.AbstractEntity; |
07 | import com.ho.annotations.data.entity.BookEntity; |
08 | import com.ho.annotations.data.entity.ErrorEntity; |
09 | import com.ho.annotations.validator.ValidatorUtil; |
11 | public class TestValidator { |
13 | public static void main(String[] args) { |
17 | System.out.println( "----- Test n°1 : All fields are filled ----" ); |
18 | final BookEntity book = new BookEntity(); |
20 | book.setName( "Germinal" ); |
21 | book.setPrice( 123.23 ); |
22 | book.setNumberOfRead( 12 ); |
23 | book.setLastReadDateTime(GregorianCalendar.getInstance()); |
24 | book.setAuthors( new String[]{ "Emile Zola" }); |
25 | ValidatorUtil.validateEntity(book); |
31 | System.out.println( "----- Test n°2 : Some optional fields are missing ----" ); |
32 | final BookEntity book = new BookEntity(); |
34 | book.setName( "Germinal" ); |
35 | book.setPrice( 123.23 ); |
36 | book.setNumberOfRead( 12 ); |
37 | book.setLastReadDateTime(GregorianCalendar.getInstance()); |
38 | ValidatorUtil.validateEntity(book); |
44 | System.out.println( "----- Test n°3 : Some mandatory fields are missing ----" ); |
45 | final BookEntity book = new BookEntity(); |
48 | book.setPrice( 123.23 ); |
49 | book.setNumberOfRead( 12 ); |
50 | book.setLastReadDateTime(GregorianCalendar.getInstance()); |
51 | book.setAuthors( new String[]{ "Emile Zola" }); |
52 | ValidatorUtil.validateEntity(book); |
58 | System.out.println( "----- Test n°4 : All fields are filled but a field's value is too large (maxlength is reached) ----" ); |
59 | final BookEntity book = new BookEntity(); |
61 | book.setName( "dsf sdf ezrezk jlezjr ezrezlrjezlrjezlrjez lrjezlkjr ezrezjrezrezrez" ); |
62 | book.setPrice( 123.23 ); |
63 | book.setNumberOfRead( 12 ); |
64 | book.setLastReadDateTime(GregorianCalendar.getInstance()); |
65 | book.setAuthors( new String[]{ "Emile Zola" }); |
66 | ValidatorUtil.validateEntity(book); |
75 | private static void printErrors(AbstractEntity myEntity){ |
76 | if (myEntity!= null && myEntity.hasError()){ |
77 | for (Iterator<ErrorEntity> iterator = myEntity.getErrors().iterator(); iterator.hasNext();) { |
78 | ErrorEntity currErrorEntity = iterator.next(); |
79 | System.out.println(currErrorEntity); |
82 | System.out.println( "No error found in the validation of this entity!" ); |
The outputs are:
1 | ----- Test n°1 : All fields are filled ---- |
2 | No error found in the validation of this entity! |
3 | ----- Test n°2 : Some optional fields are missing ---- |
4 | No error found in the validation of this entity! |
5 | ----- Test n°3 : Some mandatory fields are missing ---- |
7 | ----- Test n°4 : All fields are filled but a field's value is too large (maxlength is reached) ---- |
8 | NAME Value is too large. Max length is: 10 |
That’s all!!!!
Huseyin OZVEREN
Related