引言
很多应用都需要对传入的数据进行验证,web应用对这种需要更为强烈。提高验证代码的内聚性和高可重用性是一个比较重要的话题。本文提出了一种利用Annotation机制对Java对象的属性进行合法性验证的新方法。
设计目标
如果有大量的属性需要对其合法性进行验证,不可能进行硬编码,同时,业务代码也不应该感知这种验证的存在。但是需要有一个接口供业务代码设置验证的逻辑。并且,某个验证逻辑可以重用到多处变量。还需要达到验证逻辑的集中,接口清晰,便于后续的维护和添加。
设计策略
利用Annotation,可以设置java类的某个实例变量多个验证逻辑,通用的验证框架会调用每一种验证逻辑对该属性进行验证。
主要接口
如何提供公共的框架来调用每一种验证逻辑对某个对象的实例变量进行验证?接口可以给我们提供这样的方便。一个很直观的接口是FieldChecker。
public interface FieldChecker {
/**
* 根据某个验证规则,验证某个实例变量的值得合法性。
* @param field 实例变量
* @param value 变量的值
* @param annotation 验证规则的annotation
* @return
*/
public CheckResult check(Field field,Object value,Annotation annotation);
}
不同的验证规则只需要实现这个接口即可。
CheckResult是验证的结果。
public class CheckResult {
/**
* 验证正确的结果,为减少实例的创建,这个为单例模式
*/
public static CheckResult SUCCESS = new CheckResult();
private boolean success = true;
private String errorMessage = null;
private CheckResult(){}
public CheckResult(String errorMessage){
this.success = false;
this.errorMessage = errorMessage;
}
public boolean isSuccess(){
return success;
}
public String getErrorMessage(){
return this.errorMessage;
}
}
我们用一个工厂接口来生成FieldChecker实例。
public interface FieldCheckerFactory {
/**
* 得到一个FieldChecker的实例
* @return
*/
public FieldChecker getChecker();
}
还需要一个管理工厂的集中类,根据某个Annotation,得到其对应的FieldCheckerFactory,因此,我们创建了FieldCheckerFactories。
public class FieldCheckerFactories {
private static Map<Class,FieldCheckerFactory> factories = new HashMap<Class,FieldCheckerFactory>();
/**
* 注册一个工厂到某个验证规则上
* @param clazz
* @param factory
*/
public static void registFactory(Class clazz,FieldCheckerFactory factory){
factories.put(clazz, factory);
}
/**
* 根据某个验证规则,得到其对应的工厂类实例
* @param clazz
* @return
*/
public static FieldCheckerFactory getFactory(Class clazz){
return factories.get(clazz);
}
}
下面展示一个Time验证的Annotation
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Time{
public static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
static FieldCheckerFactory factory = new TimeCheckerFactory();
static class TimeCheckerFactory implements FieldCheckerFactory{
public TimeCheckerFactory(){
FieldCheckerFactories.registFactory(Time.class, this);
}
/**
* 真正的属性检查规则实现
*/
private static FieldChecker checker = new FieldChecker() {
public CheckResult check(Field field, Object value, Annotation annotation) {
if(value == null) return CheckResult.SUCCESS;
Time time = (Time)annotation;
String timeValue = value.toString();
try{
new SimpleDateFormat(Time.FORMAT).parse(timeValue);
return CheckResult.SUCCESS;
}catch(Throwable e){
return new CheckResult("field:["+field.getName()+"] is not valid time string");
}
}
};
public FieldChecker getChecker(){
return checker;
}
}
}
真正的通用验证框架是ObjectChecker
public class ObjectChecker {
public static ObjectChecker checker = new ObjectChecker();
private ObjectChecker(){}
public static ObjectChecker instance(){
return checker;
}
/**
* 依次检查
* @param obj
* @return
*/
public CheckResult check(Object obj){
Field[] fields = obj.getClass().getDeclaredFields();
if(fields == null || fields.length == 0) return CheckResult.SUCCESS;
for(Field field:fields){
CheckResult result = checkField(obj,field);
if(!result.isSuccess()) return result;
}
return CheckResult.SUCCESS;
}
/**
* 检查某个属性。依次检查该属性的每一个检验规则,
* 如果遇到一个规则检查失败,则立即返回。也就是一种fast-fail的方式。
* 如果所有的规则都检查通过,则返回成功的CheckResult。
* @param obj
* @param field
* @return
*/
private CheckResult checkField(Object obj,Field field){
Annotation[] annotations = field.getAnnotations();
if(annotations == null || annotations.length == 0) return CheckResult.SUCCESS;
for(Annotation annotation:annotations){
CheckResult result = checkField(obj, field, annotation);
if(!result.isSuccess()) return result;
}
return CheckResult.SUCCESS;
}
/**
* 检查某个属性的某个规则
* @param obj
* @param field
* @param annotation
* @return
*/
private CheckResult checkField(Object obj,Field field,Annotation annotation){
FieldCheckerFactory factory = FieldCheckerFactories.getFactory(annotation.annotationType());
if(factory == null) return CheckResult.SUCCESS;
FieldChecker checker = factory.getChecker();
if(checker == null) return CheckResult.SUCCESS;
return checker.check(field, getFieldValue(obj, field), annotation);
}
private static Object getFieldValue(Object obj,Field field){
field.setAccessible(true);
try {
return field.get(obj);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
}
下面是一个简单的测试用例:
public class ObjectCheckerTest extends TestCase {
public void testChecker(){
Person person = new Person();
assertFalse((ObjectChecker.instance().check(person).isSuccess()));
person.birthday = new SimpleDateFormat(Time.FORMAT).format(new Date(System.currentTimeMillis()));
assertFalse(ObjectChecker.instance().check(person).isSuccess());
person.name = "bobo";
assertTrue(ObjectChecker.instance().check(person).isSuccess());
}
public static class Person{
@Time
private String birthday;
@NotNull
private String name;
public Person(){
this.birthday = (new SimpleDateFormat("yyyy-MM-dd")).format(new Date(System.currentTimeMillis()));
}
}
}
分享到:
相关推荐
学习JPA——Hibernate_Annotation使用实例学习JPA——Hibernate_Annotation使用实例学习JPA——Hibernate_Annotation使用实例
annotationProcessor 继承 自动创建类,获取注解对象,持续更新中
该代码是Android Annotation 实例代码,便于你理解注解
Spring IOC Annotation 注入 学习实例
NULL 博文链接:https://pan-java.iteye.com/blog/335134
JavaAnnotation实例.docx
Java Annotation的讲解和例子~~~
Java annotation 什么是java annotation?annotation 的7种标注类型。nnotation提供了一条与程序元素关联任何信息或者任何元数据(metadata...annotation类型是一种接口,能够通过java反射API的方式提供对其信息的访问。
博文链接:https://flym.iteye.com/blog/174358
安卓中生成自己的annotation(注解)并调用的demo
java annotation 从JDK5.0后提供的功能。里面包含的实例,我是为了学习这个,所以放到这个上面作为中转。从别人博客里面摘取,博客地址里面附有。
NULL 博文链接:https://baobeituping.iteye.com/blog/1201798
Spring Annotation
Java注解Annotation用起来很方便,也越来越流行,由于其简单、简练且易于使用等特点,很多开发工具都提供了注解功能,不好的地方就是代码入侵比较严重,所以使用的时候要有一定的选择性。 这篇文章将利用注解,来做...
spring 使用annotation替代xml配置实例
编译时注解demo.APT 是一种处理注解的工具,它对源代码文件进行检测找出其中的 Annotation,再根据注解自动生成代码。
赠送jar包:jakarta.annotation-api-1.3.5.jar; 赠送原API文档:jakarta.annotation-api-1.3.5-javadoc.jar; 赠送源代码:jakarta.annotation-api-1.3.5-sources.jar; 赠送Maven依赖信息文件:jakarta.annotation...
@androidx.annotation.NonNull 缺失的兼容、androidx.annotation兼容包
利用annotation(注解)开放简化java开放步骤
annotation插件配置方法与实例,这是sun的apt的注解的高级应用。