API
默认包:javax.validation
。
Validator
基础接口:javax.validation.Validator
。
public interface Validator {/** 验证 object*/<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);/** 验证属性*/<T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups);/** 验证属性值为指定value*/<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups);/** 获取指定类的BeanDescriptor*/BeanDescriptor getConstraintsForClass(Class<?> clazz);<T> T unwrap(Class<T> type);ExecutableValidator forExecutables();
}
用于验证的方法
<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);<T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups);<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups);
用于验证方法、构造函数的方法
ExecutableValidator forExecutables();
返回的是 javax.validation.executable.ExecutableValidator
。
public interface ExecutableValidator {/** 校验方法的所有参数*/<T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups);/** 校验方法的返回值*/<T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups);/** 校验构造函数的参数*/<T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups);/** 校验构造函数的返回值*/<T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups);
}
//orderService:Object ,placeOrder:Method
executableValidator.validateParameters(orderService, placeOrder, new Object[] { null, item1, 1 }, Group);
@Interceptor
public class SampleMethodInterceptor {@Injectprivate Validator validator;@AroundInvokepublic Object invoke(MethodInvocation invocation) throws Throwable {// Avoid Validator invocation on FactoryBean.getObjectType/isSingletonif (isFactoryBeanMetadataMethod(invocation.getMethod())) {return invocation.proceed();}Class<?>[] groups = determineValidationGroups(invocation);// Standard Bean Validation 1.1 APIExecutableValidator execVal = this.validator.forExecutables();Method methodToValidate = invocation.getMethod();Set<ConstraintViolation<Object>> result;try {//校验参数result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups);}catch (IllegalArgumentException ex) {// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011// Let's try to find the bridged method on the implementation class...methodToValidate = BridgeMethodResolver.findBridgedMethod(ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups);}if (!result.isEmpty()) {throw new ConstraintViolationException(result);}Object returnValue = invocation.proceed();//校验返回值result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);if (!result.isEmpty()) {throw new ConstraintViolationException(result);}return returnValue;}
}
groups
group允许在验证过程中限制约束策略的验证。在验证的过程中,group会作为参数传入,仅属于指定Group的约束才会验证。如果不指定,则是Default
Group。
当指定多个Group时,验证顺序是不受控制的。
ConstraintViolation
ConstraintViolation
用于描述验证失败的信息。
public interface ConstraintViolation<T> {String getMessage();String getMessageTemplate();T getRootBean();Class<T> getRootBeanClass();Object getLeafBean();Object[] getExecutableParameters();Object getExecutableReturnValue();Path getPropertyPath();Object getInvalidValue();ConstraintDescriptor<?> getConstraintDescriptor();<U> U unwrap(Class<U> type);
}
getMessage
:返回验证失败的本地化的message。getMessageTemplate
:返回非解析的message(一般是约束的message属性)。框架使用此值作为错误码key。getRootBean
:返回导致验证失败的根对象(例如传给Validator.validate()
方法的object
参数)。对于方法验证,返回的是正在执行方法的对象,对于构造函数或Validator.validateValue()
,返回null。getRootBeanClass
:rootBean
的Class。对于构造函数返回构造函数所在的类。getLeafBean
:返回以下对象:- bean约束,则返回约束应用的bean实例
- 放置在属性上的属性约束或容器元素约束,则返回属性所属的bean实例。
- 属性约束,调用
Validator.validateValue()
,返回ConstraintViolation
,则为null。 - 方法参数、交叉参数、返回值 约束或者方法参数、返回值上放置的容器元素约束,则返回执行方法的对象。
- 构造函数的参数、交叉参数约束 或者构造函数的参数上放置的容器元素约束,则为null。
- 构造函数返回值约束,则为构造函数创建的对象实例。
getExecutableParameters
:返回方法或构造函数调用传入的参数。getExecutableReturnValue
:返回方法或构造函数调用的返回值getInvalidValue
:返回传给isValid()
的值,即被验证的值。对于交叉验证参数,则返回调用传入的参数数组。getConstraintDescriptor
:约束的元数据getPropertyPath
:返回从根对象到验证失败的对象的路径。unwrap
:
Path
public interface Path extends Iterable<Path.Node> {@OverrideString toString();/*** Represents an element of a navigation path.*/interface Node {String getName();boolean isInIterable();Integer getIndex();Object getKey();ElementKind getKind();<T extends Node> T as(Class<T> nodeType);@OverrideString toString();}interface MethodNode extends Node {List<Class<?>> getParameterTypes();}interface ConstructorNode extends Node {List<Class<?>> getParameterTypes();}interface ReturnValueNode extends Node {}interface ParameterNode extends Node {int getParameterIndex();}interface CrossParameterNode extends Node {}interface BeanNode extends Node {/** 获取bean的容器类型*/Class<?> getContainerClass();/** 获取 类型参数的index。*/Integer getTypeArgumentIndex();}/*** Node representing a property.** @since 1.1*/interface PropertyNode extends Node {Class<?> getContainerClass();Integer getTypeArgumentIndex();}/** 容器元素节点*/interface ContainerElementNode extends Node {Class<?> getContainerClass();Integer getTypeArgumentIndex();}
}
Path
为Node
的可迭代集合。
Node
Node提供方法:
getName()
:返回Node的表示。isInIterable()
:如果node表示一个包含在数组、多值容器(e.g. Iterable,Map)中的元素,则返回true。getIndex()
:返回元素在容器中的索引,或者 nullgetKey()
:返回元素在容器中的key,或者null。getKind()
:返回node 的类型as(Class<? extends Node>)
:node 转换为指定子类型。
Node的子类型:
- BeanNode
- PropertyNode
- MethodNode
- ConstructorNode
- ParameterNode
- CrossParameterNode
- ReturnValueNode
- ContainerElementNode
元素类型
public enum ElementKind {BEAN,PROPERTY,METHOD,CONSTRUCTOR,PARAMETER,CROSS_PARAMETER,RETURN_VALUE,CONTAINER_ELEMENT
}
Path生成规则
- 实例类型考虑的是运行时类型(实例的真实类型)而不是静态类型(声明类型)。
- 如果失败Node是根对象,则
BeanNode
的name
为null,KIND
为ElementKind.BEAN
- 当级联验证时:
- 级联属性时,添加一个PropertyNode,name为字段名或属性名。Kind为
ElementKind.PROPERTY
。 - 级联容器时,有index时,添加一个
getIndex()
返回不为null 的Node。 - 级联容器时,有key时,添加一个
getKey()
返回不为null 的Node。 - 级联可枚举容器时,返回
isInIterable()
为true 的Node - … …
- 级联属性时,添加一个PropertyNode,name为字段名或属性名。Kind为
- 嵌套容器时
- 属性级别约束,返回 PropertyNode,name为字段或者属性名
- 类级别约束,返回 BeanNode,name为null。
- 方法,构造函数约束,
- 容器元素约束。
示例
Message插值(Message interpolation)
Message插值用于把约束的Message属性解析成可读的,完整的Message。
默认Message插值
每个符合Bean验证的实现都包含一个默认的消息插值,遵守规范的算法来解析消息。
消息插值的前提条件:
- 每个约束必须通过
message
属性定义消息 - 每个约束的
message
属性必须定义一个默认值。 - 在声明约束的时候,可以覆盖
message
属性。
message
格式
message
是个字符串,支持参数。参数用{}
或${}
定义。特定字符需要使用转义符。\\,\{,\},\$
Value must be between {min} and {max}
Must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
默认算法
-
从message属性中抽取参数,作为key从命名为
ValidationMessages
(通常使用属性文件/ValidationMessages.properties
和它的本地变量)的ResourceBundle
查询,使用定义的locale
。如果查找到属性,则用属性值替换变量。递归应用此规则直到没有参数。 -
参数作为key从
Bean Validation provider
内置的ResourceBundle
查询属性值,使用定义的locale
。如果查找到属性,则用属性值替换变量。此规则不能递归应用。 -
如果步骤2触发一个替换,则应用步骤1,否则执行步骤4
-
消息参数如果匹配了约束的属性,则用约束声明中指定的属性值替换。参数插值优先于消息表达式
//参数 javax.validation.constraints.Size.message=size must be between {min} and {max} //声明的属性 @Size(min=1, max=50) private String title;
-
消息字符串中的消息表达式是用EL解析。
本地化
interpolate(String,Context, Locale)
//默认使用 Locale.getDefault()
消息表达式
消息表达式使用 ${}
包含起来,引用的变量需要在EL上下文中能够访问。以下属性和bean可以在EL上下文中访问:
- 约束声明时指定的属性值。
- 通过
validatedValue
引用被验证的值。 - 用过
formatter
引用格式化器。${formatter.format('%1$.2f', validatedValue)}
如果在消息插值期间发生异常,例如由于无效表达式或引用未知属性,则消息表达式保持不变。
自定义消息插值
自定义消息插值,需要实现接口MessageInterpolator
。
public interface MessageInterpolator {String interpolate(String messageTemplate, Context context);String interpolate(String messageTemplate, Context context, Locale locale);/*** Information related to the interpolation context.*/interface Context {//返回 导致验证失败的约束的metadataConstraintDescriptor<?> getConstraintDescriptor();//返回正在被验证的值Object getValidatedValue();<T> T unwrap(Class<T> type);}
}
messageTemplate
:约束声明时的message
属性值或者提供给ConstraintValidatorContext
方法的参数。
消息插值器实现必须是线程安全的。 此实例在ValidatorFactory
构造时通过 Configuration.messageInterpolator(MessageInterpolator)
设置,并且被由其构造的Validator
共用。