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 Validator
6.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: context
A 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 modeConstraintDescriptor
Is 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 insteadParanamerParameterNameProvider
Based on:com.thoughtworks.paranamer.Paranamer
Obtain 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