Java – Use Annotation to create Custom Constraint

java

Deprecated: get_the_author_email is deprecated since version 2.8.0! Use get_the_author_meta('email') instead. in /home/cloudber/public_html/wp-includes/functions.php on line 4713

Imagine, given a situation where you want to check if a required String field whether has emptied value or not. The simplest way is to use if keyword to check whether a given field which its type is String whether is emptied or null.

if (("").equals(userModel.getFirstName())){
    ......
}

In addition to the above-mentioned method, one can use annotation to do validation.

  1. Create a Custom Annotation
  2. Required.java
    import java.lang.annotation.*;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Required {
    
    }
  3. Create a Java Bean
  4. UserModel.java
    import java.io.Serializable;
    
    public class UserModel implements Serializable {
    
    	private static final long serialVersionUID = -4469704917708123018L;
    
    	private long id = 0;
    	
    	private String firstName = "";
    	
    	@Required
    	private String lastName = "";
    	
    	public long getId() {
    		return id;
    	}
    	public void setId(long id) {
    		this.id = id;
    	}
    	public String getFirstName() {
    		return firstName;
    	}
    	public void setFirstName(String firstName) {
    		this.firstName = firstName;
    	}
    	public String getLastName() {
    		return lastName;
    	}
    	public void setLastName(String lastName) {
    		this.lastName = lastName;
    	}
    }
    

    In this example, I m going to make onlylastName as a required field. Which means the value of lastName should not be emptied.

  5. A Repository Class which contains Validator
  6. UserRepository.java
    import java.lang.reflect.Field;
    
    public class UserRepository {
    	public static void register(UserModel userModel, ValidationMode validationMode) {
    		if (null  == userModel) {
    			return;
    		}
    		if (validationMode == ValidationMode.BRANCHING) {
    			validateWithBranching(userModel);
    		}
    		
    		if (validationMode == ValidationMode.ANNOTATION) {
    			validateWithAnnotation(userModel);
    		}
    		
    	}
    	
    	public static void validateWithBranching(UserModel userModel) {
    		if (("").equals(userModel.getFirstName())) {
    			throw new IllegalArgumentException("First Name is required");
    		}
    		
    		if (("").equals(userModel.getLastName())) {
    			throw new IllegalArgumentException("Last Name is required");
    		}
    	}
    	
    	public static void validateWithAnnotation(UserModel userModel) {
    		final Field[] modelFields = userModel.getClass().getDeclaredFields();
    
            for (Field modelField : modelFields) {
            	if (modelField.isAnnotationPresent(Required.class)) {
            		modelField.setAccessible(true);
            		try {
            			String value = (String) modelField.get(userModel);
            			if ("".equals(value)) {
                            throw new IllegalArgumentException(modelField + " is required");
            			}
            		}
            		catch(IllegalAccessException  ex) {
            			ex.printStackTrace();
            		}
            	}
            }
    	}
    	
    }
    

    The main points of this class I would like to emphasize are the following:

    • userModel.getClass().getDeclaredFields()the getClass method returns an instance of Class, which has a method called getDeclaredFields that returns all attributes (even private) of this class.
    • modelField.isAnnotationPresent(Required.class)as the method name explains, this is where it’s verified the presence of the Required annotation for each attribute of the model being validated.
    • modelField.setAccessible(true)this makes a private field accessible, meaning we can read or change itsvalue.
    • modelField.get(userModel)it returns the value of the modelField attribute in any object instance
  7. Testing the codes
  8. UserRepositoryTest
    import static org.junit.jupiter.api.Assertions.assertThrows;
    
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.Test;
    
    
    class UserRepositoryTest {
    	static UserModel userModel;
    	 	
    	@BeforeAll
    	static void init() {
    		userModel = new UserModel();
    	}
    	
    	@Test
    	void testValidateWithBranchingMissingValue() {
    			
    		assertThrows(IllegalArgumentException.class, () -> {
    			UserRepository.register(userModel, ValidationMode.BRANCHING);
    	    });	
    	}
    	
    	@Test
    	void testValidateWithAnnotationMissingValue() {
    		
    		assertThrows(IllegalArgumentException.class, () -> {
    			UserRepository.register(userModel, ValidationMode.ANNOTATION);
    	    });	
    	}
    
    }

References

Java Custom Annotations
Java Reflection API

Source Codes

GitHub

Author: Wu Chia Chong

My main areas of interest are software architectures and software design methods, patterns, and new trends in software development.

Author: Wu Chia Chong

My main areas of interest are software architectures and software design methods, patterns, and new trends in software development.