At the use level, you need to be familiar with the standard interfaces of Bean Validation

Jordan is the God of basketball I've heard of, and Kobe is the God of basketball I've seen with my own eyes. This article has been https://www.yourbatman.cn Included, there are small and beautiful columns such as Spring technology stack, MyBatis, JVM and middleware for free learning. Pay attention to the official account [BAT's Utopia] and break it one by one, grasp it deeply, and refuse to taste it.

catalogue

✍ preface

Hello, I'm YourBatman.

Through the narration of the first two articles, I believe it can arouse your interest in Bean Validation. From the perspective of a user, what interfaces and interface methods should we master and be familiar with if we want to use Bean Validation to complete verification?

Version Convention

  • Bean Validation version: 2.0.2
  • Hibernate Validator version: 6.1.5 Final

✍ text

Bean Validation belongs to the Java EE standard technology and has the corresponding JSR abstraction. Therefore, in the actual use process, we only need to be standard oriented, and we don't need to care about the specific implementation (whether it is hibernate implementation or apache implementation is not important), that is, we often say interface oriented programming.

Tips: in order to facilitate the following example explanation, some simple and common methods are extracted as follows:

public abstract class ValidatorUtil {

    public static ValidatorFactory obtainValidatorFactory() {
        return Validation.buildDefaultValidatorFactory();
    }
    
    public static Validator obtainValidator() {
        return obtainValidatorFactory().getValidator();
    }

    public static ExecutableValidator obtainExecutableValidator() {
        return obtainValidator().forExecutables();
    }

    public static <T> void printViolations(Set<ConstraintViolation<T>> violations) {
        violations.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()).forEach(System.out::println);
    }

}

Validator

Verifier interface: the entry of verification, which can complete the verification of Java Bean, a property, method, constructor, etc.

public interface Validator {
	...
}

It is an API that users contact most, and of course, it is also the most important. Therefore, each method is explained below + use examples.

Validate: validate Java beans

<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);

Validate all constraints on the Java Bean object. Examples are as follows:

Java Bean:

@ScriptAssert(script = "_this.name==_this.fullName", lang = "javascript")
@Data
public class User {

    @NotNull
    private String name;
    
    @Length(min = 20)
    @NotNull
    private String fullName;
}

@Test
public void test5() {
    User user = new User();
    user.setName("YourBatman");

    Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validate(user);
    ValidatorUtil.printViolations(result);
}

Note: @ scriptasset is a script constraint annotation provided by Hibernate Validator. It can realize the logical verification of broken fields. It is very powerful. It will be explained in detail later

Run the program, console output:

Execute script expression"_this.name==_this.fullName"The expected result was not returned: User(name=YourBatman, fullName=null)
fullName Cannot be null: null

Meet expectations. It is worth noting that for the @ Length constraint in fullName, null is legal, so there will be no corresponding log output

Verify all constraints in the Java Bean, including:
1. Constraints on attributes
2. Constraints on classes

validateProperty: validates the specified property

<T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups);

Verify all constraints on a property in a Java Bean. Examples are as follows:

@Test
public void test6() {
    User user = new User();
    user.setFullName("YourBatman");

    Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validateProperty(user, "fullName");
    ValidatorUtil.printViolations(result);
}

Run the program, console output:

fullName The length needs to be between 20 and 2147483647: YourBatman

Meet expectations. It will verify all constraints on the attribute. Note that it is only on the attribute, regardless of other places.

validateValue: verify the value

Verify whether a value meets all constraints on the specified attribute. If I assign this value to this attribute, is it legal?

<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
										  	String propertyName,
										  	Object value,
										  	Class<?>... groups);

This verification method is special: it does not need to have an object instance first, but directly verify whether a value meets all the constraints of an attribute, so it can do things and check and judge. It is still very useful. Examples are as follows:

@Test
public void test7() {
    Set<ConstraintViolation<User>> result = ValidatorUtil.obtainValidator().validateValue(User.class, "fullName", "A brother");
    ValidatorUtil.printViolations(result);
}

Run the program, output:

fullName The length needs to be between 20 and 2147483647: A brother

If the procedure is changed to: validateValue(User.class, "fullName", "YourBatman-YourBatman");, Run the program again, and the console will no longer output (if the string length exceeds 20, it is legal).

Get Class type description information

BeanDescriptor getConstraintsForClass(Class<?> clazz);

This clazz can be a class or interface type. BeanDescriptor: describes the constrained Java Bean and the constraints associated with it. Examples are as follows:

@Test
public void test8() {
    BeanDescriptor beanDescriptor = obtainValidator().getConstraintsForClass(User.class);
    System.out.println("Whether this class needs verification:" + beanDescriptor.isBeanConstrained());

    // Get the constraints of properties, methods and constructors
    Set<PropertyDescriptor> constrainedProperties = beanDescriptor.getConstrainedProperties();
    Set<MethodDescriptor> constrainedMethods = beanDescriptor.getConstrainedMethods(MethodType.GETTER);
    Set<ConstructorDescriptor> constrainedConstructors = beanDescriptor.getConstrainedConstructors();
    System.out.println("Attributes to be verified:" + constrainedProperties);
    System.out.println("Methods to be verified:" + constrainedMethods);
    System.out.println("Constructor to be verified:" + constrainedConstructors);

    PropertyDescriptor fullNameDesc = beanDescriptor.getConstraintsForProperty("fullName");
    System.out.println(fullNameDesc);
    System.out.println("fullName Number of constraint annotations for attribute:"fullNameDesc.getConstraintDescriptors().size());
}

Run the program, output:

Whether this class needs verification: true
 Attributes to be verified:[PropertyDescriptorImpl{propertyName=name, cascaded=false}, PropertyDescriptorImpl{propertyName=fullName, cascaded=false}]
Methods to be verified:[]
Constructor to be verified:[]
PropertyDescriptorImpl{propertyName=fullName, cascaded=false}
fullName Number of constraint annotations for attribute: 2

Get Executable verifier

@since 1.1
ExecutableValidator forExecutables();

For the validation method of bean and Validator, it can only return the value of 1.0.

The ExecutableValidator API is provided in version 1.1 to solve such requirements. Its instance can be obtained by calling the Validator method, which is very convenient. Please move on to the specific use of ExecutableValidator Last article.

ConstraintViolation

Details of constraint violations. This object holds the context of the constraint violation and the description message.

// <T>: root bean
public interface ConstraintViolation<T> {
}

In short, it saves the results after all constraints are executed (whether Java Bean constraints, method constraints, etc.), and provides an API to access the results. It is relatively simple:

Tip: this object will only be generated if the constraint is violated. Violation of a constraint corresponds to an instance

// Messages that have been interpolated
String getMessage();
// Message template without interpolation (variables in it have not been replaced, if any)
String getMessageTemplate();

// Property path starting from rootBean. For example: parent fullName
Path getPropertyPath();
// Tell which constraint failed
ConstraintDescriptor<?> getConstraintDescriptor();

Example: omitted.

ValidatorContext

Validator context, and create validator instance based on this context. Different contexts can create different instances (different here refers to different internal components) to meet various personalized customization needs.

ValidatorContext interface provides setting methods to customize the core components of Validator, which are the five core components of Validator validator:

public interface ValidatorContext {
	ValidatorContext messageInterpolator(MessageInterpolator messageInterpolator);
	ValidatorContext traversableResolver(TraversableResolver traversableResolver);
	ValidatorContext constraintValidatorFactory(ConstraintValidatorFactory factory);
	ValidatorContext parameterNameProvider(ParameterNameProvider parameterNameProvider);
	ValidatorContext clockProvider(ClockProvider clockProvider);
	
	// @since 2.0 value extractor.
	// Note: it is the add method, which belongs to add
	ValidatorContext addValueExtractor(ValueExtractor<?> extractor);
	Validator getValidator();
}

Different component implementations can be set through these methods. After setting, a getValidator() will get a customized validator, which is no longer the same. Therefore, the first step is to get the ValidatorContext instance. Here are two methods.

Method 1: own new

@Test
public void test2() {
    ValidatorFactoryImpl validatorFactory = (ValidatorFactoryImpl) ValidatorUtil.obtainValidatorFactory();
    // Use the default Context and initialize a Validator instance
    // A factory instance must be passed in
    ValidatorContext validatorContext = new ValidatorContextImpl(validatorFactory)
            .parameterNameProvider(new DefaultParameterNameProvider())
            .clockProvider(DefaultClockProvider.INSTANCE);

    // Generate validator instances through this context (Note: multiple calls generate multiple instances)
    System.out.println(validatorContext.getValidator());
}

Run the program, console output:

org.hibernate.validator.internal.engine.ValidatorImpl@1757cd72

This is the most direct way. You can new whatever you want. However, such use is flawed, which is mainly reflected in these two aspects:

  1. Not abstract enough. The new method has nothing to do with abstraction
  2. The API of Hibernate Validator is strongly coupled, such as: org hibernate. validator. internal. engine. ValidatorContextImpl#ValidatorContextImpl

Method 2: Factory generated

Even if the ValidatorContext instance is obtained through its own new method, it needs to be passed into the validator factory, so it's better to directly use the factory to generate it. ValidatorFactory also provides corresponding methods:

ValidatorContext usingContext();

This method is used to obtain an instance of ValidatorContext, which is highly abstract and independent of the underlying API. It is the recommended acquisition method, and has the effect of streaming programming when used, as shown below:

@Test
public void test3() {
    Validator validator = ValidatorUtil.obtainValidatorFactory().usingContext()
            .parameterNameProvider(new DefaultParameterNameProvider())
            .clockProvider(DefaultClockProvider.INSTANCE)
            .getValidator();
}

Obviously, this method is recommended.

Get two poses for the Validator instance

At the end of the article, look back at the two poses obtained by the validator instance. Validator validator interface is the main API to complete data verification (Java Bean verification, method verification, etc.). After the above description, we can summarize the acquisition methods below.

Method 1: directly obtained by the factory

@Test
public void test3() {
    Validator validator = ValidatorUtil.obtainValidatorFactory().getValidator();
}

This method is very simple, simple, very friendly to beginners, simple to get started and has obvious advantages. All components use the default mode, which is easy to worry about. If you want to pick shortcomings, there must be some: you can't meet the needs of personalization and customization. To put it bluntly, you can't customize the implementation of the five components + value extractor.

As such an excellent Java EE standard technology, how can we be less open to extensions? Continue with mode 2~

Method 2: get from context

Validator context is ValidatorContext. Its steps are to get the context instance first, then customize it, and then create the validator validator instance through the context instance.

Example code:

@Test
public void test3() {
    Validator validator = ValidatorUtil.obtainValidatorFactory().usingContext()
            .parameterNameProvider(new DefaultParameterNameProvider())
            .clockProvider(DefaultClockProvider.INSTANCE)
            .getValidator();
}

This method gives great customization. You can specify the implementation of core components to meet your own requirements.

The combination of these two methods is a typical combination of default + custom extension? In addition, validators are thread safe. Generally speaking, an application only needs to initialize one Validator instance, so it is recommended to use method 2 for initialization, which is more friendly to personality extension.

✍ summary

This article looks at how to use Bean Validation from the perspective of users and what standard interface API s must be mastered. With these knowledge points, most case s can deal with it freely at ordinary times.

Specification interface / standard interface can generally solve most problems, which is the boundary of specification. Some can be used and some not

Of course, these are basic skills. To deeply understand the function of Bean Validation, we must deeply understand the implementation of Hibernate Validator, because some commonly used case s are well supplemented. See below.

✔ Recommended reading:

Tags: Bean Validation Hibernate Validator

Posted by scrap0346 on Thu, 19 May 2022 08:54:39 +0300