Annotation

@Retention(RUNTIME)  // annotated info store in class level . runtime get
@Target({ FIELD, PARAMETER }) // annotated used at filed parameter of method
@Constraint(validatedBy=MyValidator.class) //validation method
public @interface CountryCode {

	String message() default "{app.countrycode.message}"; //key  for message 
	
	Class<?>[] groups() default { };
	
	Class<? extends Payload>[] payload() default { };
	
	
	PlatForm value() default PlatForm.USA;  // self defined attribute
}

Validator

public class MyValidator implements ConstraintValidator<CountryCode, String> {
									// < annotated  , checked object >
	private PlatForm countryCode;
	
	@Override
	public void initialize(CountryCode constraintAnnotation) {//may be needed to overwrite	
		countryCode=constraintAnnotation.value(); //get annotated attribute
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {	
		//checked result
		String v= Optional.ofNullable(value).orElse("").toUpperCase();
		switch(countryCode) {
		case USA:
			return "USA".equals(v);		
		case CAN:
			return "CAN".equals(v);				
		default:
			return false;			
		}
	}

}

ValidationMessages.properties

app.countrycode.message=The CountryCode is expect {value} but is ${validatedValue.toUpperCase()}