Difficulty is a spring, you weak it is strong. This article has been https://www.yourbatman.cn included, along with all the inside have Spring technology stack, MyBatis, middleware, such as the JVM, small and beautiful column study for free. Pay attention to the public number [BAT utopia] one by one to break, in-depth grasp,

The foreword ✍

Hi, I’m YourBatman.

In the previous article, we introduced the ValidatorContext, which allows you to customize each of the five core components of the Validator. In this article, we’ll take a look at what role these core components play in the validation process.

As a core component, it is necessary to explore a bit more. Based on this, it is easy to learn and use other functional modules. But it is true that the process is boring, so you need to persevere.

Version of the agreement

  • Bean Validation version:2.0.2
  • Hibernate Validator6.1.5. The Final

✍ body

The five core components of the validator can be set separately with the ValidatorContext: if not set (or null), fall back to the component that uses the ValidatorFactory default.

Prepared components, all exposed through the ValidatorFactory for access:

public interface ValidatorFactory extends AutoCloseable {...MessageInterpolator getMessageInterpolator(a);
	TraversableResolver getTraversableResolver(a);
	ConstraintValidatorFactory getConstraintValidatorFactory(a);
	ParameterNameProvider getParameterNameProvider(a);
	@since 2.0
	ClockProvider getClockProvider(a); . }Copy the code

MessageInterpolator

Message interpolator. It’s not very literal: simply format the message content and perform substitution and evaluation if there is a placeholder {} or el expression ${}. Grammatical errors should be tolerated as much as possible.

Failed message templates are handed to it as a human-readable message format, so it can handle message internationalization: messages have the same key, but different message templates are displayed for different locales. Finally, a placeholder in the replacement/technical template is enough

This is the standard interface for Bean Validation, which Hibernate Validator provides:Hibernate Validation it USES ResourceBundleMessageInterpolator supports both parameters, also support the EL expression. Internally usedjavax.el.ExpressionFactoryThis API supports EL expressionsThe ${}As follows:must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}It is able to calculate dynamically${inclusive == true ? 'or equal to ' : ''}This is the value of theta.

public interface MessageInterpolator {
	String interpolate(String messageTemplate, Context context);
	String interpolate(String messageTemplate, Context context, Locale locale);
}
Copy the code

Interface methods are straightforward: populate the messageTemplate messageTemplate based on the Context. Its specific working process is illustrated as follows: contextA context typically has a key-value pair of the key that needs to be replaced, as shown in the following figure:Hibernate’s implementation of ContextExtend outTwo maps of the figure (non-JSR standard) allow you toPrior to theConstraintDescriptor, cannot fallback to the standard modeConstraintDescriptorIs the attribute value of the annotation. The specific value code is as follows:

ParameterTermResolver:private Object getVariable(Context context, String parameter) {
		// The value is extended from Hibernate first
		if (context instanceof HibernateMessageInterpolatorContext) {
			Object variable = ( (HibernateMessageInterpolatorContext) context ).getMessageParameters().get( parameter );
			if( variable ! =null ) {
				returnvariable; }}// Fallback to standard mode: values from annotation properties
		return context.getConstraintDescriptor().getAttributes().get( parameter );
	}
Copy the code

Most of the time, we only need to get the value inside the annotation property, that is, the error message can use {annotation property name} this way to dynamically get the annotation property value, give a friendly error message.

How are the Message and Expression arguments in context put in? In the later advanced usage section, the k-v substitution parameters will be customized, and the advanced application knowledge of this section will be used, as described below.

TraversableResolver

Processors that can span. The ValidationProvider determines whether a property can be accessed by the ValidationProvider. This ValidationProvider determines whether a property can be accessed by the ValidationProvider. This ValidationProvider determines whether a property can be accessed by the ValidationProvider.

public interface TraversableResolver {

	// Is reachable
	boolean isReachable(Object traversableObject, Node traversableProperty, Class
        rootBeanType, Path pathToTraversableObject, ElementType elementType);
						
	// Whether it is cascaded (whether it is annotated with @valid)
	boolean isCascadable(Object traversableObject, Node traversableProperty, Class
        rootBeanType, Path pathToTraversableObject, ElementType elementType);
}
Copy the code

This interface is not responsible for configuration items. For internal use, the caller doesn’t need to care about it or change its default mechanism.

ConstraintValidatorFactory

Constraint verifier factory. ConstraintValidator: each Constraint annotation specifies one or more Constraint validators, such as @constraint (validatedBy = {xxx.class}).

ConstraintValidatorFactory is factory: object instances can be generated according to the Class.

public interface ConstraintValidatorFactory {

	// Generate instance: the interface does not dictate how you generate<T extends ConstraintValidator<? ,? >>T getInstance(Class<T> key);
	// Release the instance. Flags that this instance is no longer needed and is generally an empty implementation
	This method is called when integrating with the Spring container.DestroyBean (instance)
	void releaseInstance(ConstraintValidator
        instance);
}
Copy the code

Hibernate provides the only realize ConstraintValidatorFactoryImpl: using the empty constructor generated instance clazz. GetConstructor (). The newInstance (); .

Tip: The interface doesn’t tell you how to generate an instance. Hibernate Validator does this using empty constructs

ParameterNameProvider

Parameter name provider. This component does exactly what Spring’s ParameterNameDiscoverer does: it gets method/constructor parameter names.

public interface ParameterNameProvider {
	
	List<String> getParameterNames(Constructor
        constructor);
	List<String> getParameterNames(Method method);
}
Copy the code

Implementation provided:

  • DefaultParameterNameProvider: Based on the Java Reflection APIExecutable#getParameters()implementation
@Test
public void test9(a) {
    ParameterNameProvider parameterNameProvider = new DefaultParameterNameProvider();

    // Get the parameterless and parameterless constructs of Person (@noargsconstructor and @allargsconstructor)
    Arrays.stream(Person.class.getConstructors()).forEach(c -> System.out.println(parameterNameProvider.getParameterNames(c)));
}
Copy the code

Run the program, output:

[arg0, arg1, arg2, arg3]
[]
Copy the code

Similarly, if you want to print out an explicit parameter name, add the -parameters parameter to the compile parameter.

  • ReflectionParameterNameProvider:expired. Use default above instead
  • ParanamerParameterNameProviderBased on:com.thoughtworks.paranamer.ParanamerObtain the parameter name, need to import the corresponding package. Well, I won’t try it here

ClockProvider

Clock provider. This interface is very simple, is to provide a Clock, @past, @future and other reading judgment reference. The only implementation is DefaultClockProvider:

public class DefaultClockProvider implements ClockProvider {

	public static final DefaultClockProvider INSTANCE = new DefaultClockProvider();

	private DefaultClockProvider(a) {}// The default is the system clock
	@Override
	public Clock getClock(a) {
		returnClock.systemDefaultZone(); }}Copy the code

By default, the current system clock is used as the reference. If your system has a global reference standard, such as a unified Clock, you can implement your own Clock Clock through this interface. After all, the time of each server is not guaranteed to be exactly the same. This is necessary for time-sensitive application scenarios such as bidding.

This is a brief description of the five core components of the Validator. The first component is the MessageInterpolator which I think is the most important and needs to be understood. Useful for customizing message templates and internationalizing messages.

Snacks: ValueExtractor

Value extractor. Version 2.0 adds an important component API for extracting values from containers. Containers include arrays, collections, maps, Optional, and so on.

// T: type of the container to be extracted
public interface ValueExtractor<T> {

	// Fetch originalValue from originalValue to receiver
	void extractValues(T originalValue, ValueReceiver receiver);

	// Provides a set of methods to receive values extracted by ValueExtractor
	interface ValueReceiver {
	
		// Receives the value extracted from the object
		void value(String nodeName, Object object);
		// Receive Iterable values, such as List, Map, Iterable, etc
		void iterableValue(String nodeName, Object object);
		// Receive indexed values, such as List Array
		// I: indicates the index
		void indexedValue(String nodeName, int i, Object object);
		// Receive the value of the key-value pair, such as Map
		void keyedValue(String nodeName, Object key, Object object); }}Copy the code

ValueExtractor has a very large number of implementation classes (all implementation classes are built in, non-public, this is the default supported container type) :Here are two typical implementations:

LIST_ELEMENT_NODE_NAME -> < List element>
class ListValueExtractor implements ValueExtractor<ListThe < @ExtractedValue? >>{

	static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ListValueExtractor() );

	private ListValueExtractor(a) {}@Override
	public void extractValues(List
        originalValue, ValueReceiver receiver) {
		for ( int i = 0; i < originalValue.size(); i++ ) { receiver.indexedValue( NodeImpl.LIST_ELEMENT_NODE_NAME, i, originalValue.get( i ) ); }}}// Extract the value from Optional
@UnwrapByDefault
class OptionalLongValueExtractor implements ValueExtractorThe < @ExtractedValue(type = Long.class) OptionalLong> {

	static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new OptionalLongValueExtractor() );

	@Override
	public void extractValues(OptionalLong originalValue, ValueReceiver receiver) {
		receiver.value( null, originalValue.isPresent() ? originalValue.getAsLong() : null); }}Copy the code

The Validator takes values out of the container and validates them. Since Bean Validation2.0, the Validator has supported validation of elements in the container, like this: List< @notnul@valid Person> and Optional< @notnul@valid Person>.

If you have a custom container that needs to be extracted, then you can customize a ValueExtractor implementation and add it with ValidatorContext#addValueExtractor()

✍ summary

This article mainly introduces the functions of five core components of Validator. Bean Validation2.0 provides ValueExtractor component to implement the validation of container elements. It greatly simplifies the verification complexity of container elements.

  • The first article will improve your understanding of Bean Validation data Validation
  • Validates method parameters and return values
  • 3. On the usage side, Bean Validation is the standard interface that you need to be familiar with