Java Bean Validation API
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 {
@Inject
private Validator validator;
@AroundInvoke
public Object invoke(MethodInvocation invocation) throws Throwable {
// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
return invocation.proceed();
}
Class<?>[] groups = determineValidationGroups(invocation);
// Standard Bean Validation 1.1 API
ExecutableValidator 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> {
@Override
String 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);
@Override
String 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 {
//返回 导致验证失败的约束的metadata
ConstraintDescriptor<?> getConstraintDescriptor();
//返回正在被验证的值
Object getValidatedValue();
<T> T unwrap(Class<T> type);
}
}
messageTemplate
:约束声明时的message
属性值或者提供给ConstraintValidatorContext
方法的参数。
消息插值器实现必须是线程安全的。 此实例在ValidatorFactory
构造时通过 Configuration.messageInterpolator(MessageInterpolator)
设置,并且被由其构造的Validator
共用。
触发方法验证
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!