Java Bean Validation API

2024-01-01 10:57:08

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。
  • getRootBeanClassrootBean的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();
	}
}

PathNode的可迭代集合。

Node

Node提供方法:

  • getName():返回Node的表示。
  • isInIterable():如果node表示一个包含在数组、多值容器(e.g. Iterable,Map)中的元素,则返回true。
  • getIndex():返回元素在容器中的索引,或者 null
  • getKey():返回元素在容器中的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是根对象,则BeanNodename为null,KINDElementKind.BEAN
  • 当级联验证时:
    • 级联属性时,添加一个PropertyNode,name为字段名或属性名。Kind为ElementKind.PROPERTY
    • 级联容器时,有index时,添加一个getIndex() 返回不为null 的Node。
    • 级联容器时,有key时,添加一个getKey() 返回不为null 的Node。
    • 级联可枚举容器时,返回isInIterable() 为true 的Node
    • … …
  • 嵌套容器时
  • 属性级别约束,返回 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}
默认算法
  1. 从message属性中抽取参数,作为key从命名为ValidationMessages(通常使用属性文件/ValidationMessages.properties和它的本地变量)的ResourceBundle查询,使用定义的locale。如果查找到属性,则用属性值替换变量。递归应用此规则直到没有参数

  2. 参数作为key从Bean Validation provider 内置的ResourceBundle查询属性值,使用定义的locale。如果查找到属性,则用属性值替换变量。此规则不能递归应用

  3. 如果步骤2触发一个替换,则应用步骤1,否则执行步骤4

  4. 消息参数如果匹配了约束的属性,则用约束声明中指定的属性值替换。参数插值优先于消息表达式

    //参数
    javax.validation.constraints.Size.message=size must be between {min} and {max}
    //声明的属性
    @Size(min=1, max=50)
    private String title;
    
  5. 消息字符串中的消息表达式是用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共用。

触发方法验证

文章来源:https://blog.csdn.net/demon7552003/article/details/135322738
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。