1注解使用
1.1注解的使用场景
业务输入对象需要进行一系列技术的验证如:身份证位数,电话号码位数,手机号位数,邮箱格式的验证,必输字段是否有值等。这些验证如果不抽离出来,整个业务逻辑将会是冗长的,可读性差,维护起来也繁杂,使用注解可以将这些技术性验证抽离出来,使代码更加优雅,便于维护。
1.2定义一个注解
定义一个注解非常简单,语法与定义一个接口类似:
package bizsource;
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;
@Constraint(validatedBy = ValidateID.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Identification {
String message() default "身份证号码";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
1.3定义一个注解处理器
单单定义一个注解,当然不能实现对相应数据类型的校验,需要编写注解对应的注解处理器。注解处理器实现ConstraintValidator接口,实现两个方法,其中isValid()是实际运用到的逻辑编写,该方法对注解将要注解的元素类型值进行校验,利用**@Constraint**元注解在注解类中绑定该注解处理器。
package bizsource;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
*验证类实现ConstraintValidator接口,包含两个参数:注解类和待验证的数据类型
*/
public class ValidateID implements ConstraintValidator<Identification,String> {
public void initialize(Identification constraintAnnotation) {
//初始化方法,对注解验证的初始化工作
}
public boolean isValid(String value, ConstraintValidatorContext context) {
//编写验证逻辑(简单校验)
String regularExpression = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" +
"(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";
boolean matches = value.matches(regularExpression);
return matches;
}
}
1.4定义一个注解工具类用于启动注解验证
该类中的validate()方法用于验证传入对象中所有带注解的字段值:
package utils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ValidatorUtil {
private static Validator validator = Validation.buildDefaultValidatorFactory()
.getValidator();
public static <T> Map<String, StringBuffer> validate(T obj) {//待校验对象,可以是使用了注解的bean
Map<String, StringBuffer> errorMap = null;
Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class);
if (set != null && set.size() > 0) {
errorMap = new HashMap<String, StringBuffer>();
String property = null;
for (ConstraintViolation<T> cv : set) {
//这里循环获取错误信息,可以自定义格式
property = cv.getPropertyPath().toString();
if (errorMap.get(property) != null) {
errorMap.get(property).append("," + cv.getMessage());
} else {
StringBuffer sb = new StringBuffer();
sb.append(cv.getMessage());
errorMap.put(property, sb);
}
}
}
return errorMap;
}
}
1.5测试
package app;
import bizsource.StudentInfo;
import utils.ValidatorUtil;
import java.util.Map;
public class ValidatorTest {
public static void main(String[] args) {
StudentInfo s = new StudentInfo();
long startTime = System.currentTimeMillis();
print(ValidatorUtil.validate(s));
System.out.println("===============耗时(毫秒)=" + (System.currentTimeMillis() - startTime));
s.setUserName("xinxin");
s.setAge("18a");
s.setBirthday("2016-9-1");
s.setMoney(1000.00001);
startTime = System.currentTimeMillis();
//带有注解bean被验证
print(ValidatorUtil.validate(s));
System.out.println("===============耗时(毫秒)=" + (System.currentTimeMillis() - startTime));
}
private static void print(Map<String, StringBuffer> errorMap) {
if (errorMap != null) {
for (Map.Entry<String, StringBuffer> m : errorMap.entrySet()) {
System.out.println(m.getKey() + ":" + m.getValue().toString());
}
}
}
}
2注解原理
2.1三个基本注解与四个元注解
Java中内置了三种标准注解:
- @Override表示当前方法将覆盖超类中的方法;
- @Deprecated如果使用了该注解注解的元素编译器将会发出警告信息;
- @SuppressWarnings关闭不当的编译器警告信息。
java提供四种元注解,负责新注解的创建:
- @Target表示注解可以用于什么地方,参数是ElementType枚举类,可以是CONSTRUCTOR构造器说明,FIELD域声明, LOCAL_VARIABLE局部变量声明,METHOD方法声明, PACKAGE饱声明 ,PARAMETER参数声明,TYPE类,接口或enum声明;
- @Retention表示需要在什么级别保存该注解信息,可选的RetentionPolicy参数包括SOURCE注解将被编译器丢弃;CLASS注解在class文件中可用,但是会被虚拟机丢弃;RUNTIME虚拟机在运行期也保留注解,因此可以通过反射读取注解的信息;
- @Documented将注解包含在Javadoc中;
- @Inherited允许子类继承父类中的注解
2.2注解中的元素类型限制
如1.2的注解类中使用的元素有message,groups,payload,这些元素的类型是有要求的,注解元素的可用类型如下所示:
- 所有基本类型
- String
- Class
- enum
- Annotation
- 以上类型的数组
如果使用其他类型编译器将会报错。
值得注意的是元素类型可以使用Annotation类型,这也就是说注解是可以嵌套的。
2.3嵌套注解
package bizsource;
import javax.validation.Constraint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Constraint(validatedBy = UserValidate.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnno {
String name() default "";
Identification identification() default @Identification;
}
嵌套注解怎么使用?