ParameterMessageInterpolator
资源束消息插值器,不支持el表达式,支持参数值表达式
public class ParameterMessageInterpolator extends AbstractMessageInterpolator { @Override public String interpolate(Context context, Locale locale, String term) { // 简单的说就是以$打头,就认为是EL表达式 啥都不处理 if ( InterpolationTerm.isElExpression( term ) ) { return term; } else { // 核心处理方法是context.getConstraintDescriptor().getAttributes().get( parameter ) 拿到对应的值 ParameterTermResolver parameterTermResolver = new ParameterTermResolver(); return parameterTermResolver.interpolate( context, term ); } } }
根据代码里可以看出:注解里的属性都是可以使用引用的。如@NotNull执行context.getConstraintDescriptor().getAttributes()如下:
所以你的message里可以使用{groups}、{message}。。。等占位符。比如这样写@NotNull(message = "{message} -> 名字不能为null") 结果为:{message} -> 名字不能为null -> 名字不能为null: null
ResourceBundleMessageInterpolator
它比上更加强大些,支持EL表达式,使用了javax.el.ExpressionFactory来解析它,最终是委托于InterpolationTerm来处理,具体源码此处就省略了。
Hibernate Validation它使用的是ResourceBundleMessageInterpolator来既支持参数,也支持EL表达式~
当然如果你对默认的提示词语不开心,你可以自定义自己的插值器哦~~
TraversableResolver:可移动的处理器
它的意思从字面是非常不好理解,我用粗暴的语言解释为:确定某个属性是否能被ValidationProvider访问~
注意:访问每个属性的时候它都会被调用来判断一下子~
public interface TraversableResolver { // 是否是可达的 boolean isReachable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType); // 是否是可级联的 boolean isCascadable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType); }
它的继承树如下:
ConstraintValidatorFactory:校验器工厂
校验里很重要的一个处理逻辑地是ConstraintValidato
,它就是工厂,可以根据指定的Class类型生产一个实例(其实就是调用了构造函数new出来一个~)
public interface ConstraintValidatorFactory { <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key); // 释放方法是提供给Spring容器集成时 .destroyBean(instance);的 void releaseInstance(ConstraintValidator<?, ?> instance); }
它的继承树如下
此处就只讨论ConstraintValidatorFactoryImpl
public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory { // NewInstance的run方法最终就是执行了这句话:clazz.getConstructor().newInstance()而已 // 因此最终就是创建了一个key的实例而已~ Spring相关的会把它和Bean容器结合起来 @Override public final <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { return run( NewInstance.action( key, "ConstraintValidator" ) ); } @Override public void releaseInstance(ConstraintValidator<?, ?> instance) { // noop } // 入参是个函数式接口:java.security.PrivilegedAction private <T> T run(PrivilegedAction<T> action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } }
ParameterNameProvider:参数名提供器
提供方法、构造函数的入参names们。逻辑比较简单~
public interface ParameterNameProvider { List<String> getParameterNames(Constructor<?> constructor); List<String> getParameterNames(Method method); }
它的继承树如下:
DefaultParameterNameProvider
public class DefaultParameterNameProvider implements ParameterNameProvider { @Override public List<String> getParameterNames(Constructor<?> constructor) { return doGetParameterNames( constructor ); } @Override public List<String> getParameterNames(Method method) { return doGetParameterNames( method ); } // Executable是1.8提供的抽象类。两个子类刚好就是:Method和Constructor // 因此在Java8后一个方法搞定:executable.getParameters(); private List<String> doGetParameterNames(Executable executable) { Parameter[] parameters = executable.getParameters(); List<String> parameterNames = new ArrayList<>( parameters.length ); for ( Parameter parameter : parameters ) { parameterNames.add( parameter.getName() ); } return Collections.unmodifiableList( parameterNames ); } }
ReflectionParameterNameProvider
@deprecated since 6.0,因为通过反射去获取方法名字已经是默认的了,所以可以计划移除~
If ‘-parameters’ is used at compile time, actual names will be returned. Otherwise, it will be arg0, arg1…
给个使用DefaultParameterNameProvider获取参数名们的例子:
public static void main(String[] args) { DefaultParameterNameProvider parameterNameProvider = new DefaultParameterNameProvider(); // 拿到Person的无参构造和有参构造(@NoArgsConstructor和@AllArgsConstructor) Arrays.stream(Person.class.getConstructors()).forEach(c -> { List<String> parameterNames = parameterNameProvider.getParameterNames(c); System.out.println(parameterNames); }); // Method此处我就省略不写了 }
一样的对于Java8而言,编译器’-parameters’ 才会有争取的名称~
它的效果特别像Spring的org.springframework.core.ParameterNameDiscoverer
ClockProvider
这个接口很简单,就是提供一个Clock,给@Past、@Future等判断作为参考
public class DefaultClockProvider implements ClockProvider { public static final DefaultClockProvider INSTANCE = new DefaultClockProvider(); // 单例的存在 private DefaultClockProvider() { } @Override public Clock getClock() { return Clock.systemDefaultZone(); } }
默认就是服务器当前时间。若你有参考的时间逻辑,自己提供一个ClockProvider
实现注册进Configuration即可~
ValueExtractor:值提取器
值提取器,蛮重要的,从容器内
把值提取处理啊,@since 2.0
public interface ValueExtractor<T> { // 从原始值originalValue提取到receiver里 void extractValues(T originalValue, ValueReceiver receiver); // 提供一组方法,用于接收ValueExtractor提取出来的值 // 必须将该值传递给与原始值类型对应的最佳方法。 interface ValueReceiver { // 接收从对象中提取的值。 void value(String nodeName, Object object); // 接收从未编入索引的可ITerable对象中提取的值,如List、Map、Iterable等 void iterableValue(String nodeName, Object object); // 处理List void indexedValue(String nodeName, int i, Object object); // 处理Map void keyedValue(String nodeName, Object key, Object object); } }
注意:提取值的时候,会执行doValidate完成校验。容易想到,ValueExtractor的实现类就非常之多(所有的实现类都是内建的,非public的):
示例两个如下:
// 这种泛型的写法非常有意思~int @ExtractedValue[]代表int数组 并且还是使用了@ExtractedValue标注的 class IntArrayValueExtractor implements ValueExtractor<int @ExtractedValue[]> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new IntArrayValueExtractor() ); private IntArrayValueExtractor() { } @Override public void extractValues(int[] originalValue, ValueReceiver receiver) { for ( int i = 0; i < originalValue.length; i++ ) { receiver.indexedValue( NodeImpl.ITERABLE_ELEMENT_NODE_NAME, i, originalValue[i] ); } } } class IterableValueExtractor implements ValueExtractor<Iterable<@ExtractedValue ?>> { ... @Override public void extractValues(Iterable<?> originalValue, ValueReceiver receiver) { for ( Object object : originalValue ) { receiver.iterableValue( NodeImpl.ITERABLE_ELEMENT_NODE_NAME, object ); } } } class OptionalValueExtractor implements ValueExtractor<Optional<@ExtractedValue ?>> { ... @Override public void extractValues(Optional<?> originalValue, ValueExtractor.ValueReceiver receiver) { receiver.value( null, originalValue.isPresent() ? originalValue.get() : null ); } }