CustomValidatorBean
可配置(Custom)的Bean类,也同样的实现了双接口
。它可以配置ValidatorFactory
验证器工厂、MessageInterpolator
插值器等…
public class CustomValidatorBean extends SpringValidatorAdapter implements Validator, InitializingBean { // javax.validation.ValidatorFactory @Nullable private ValidatorFactory validatorFactory; @Nullable private MessageInterpolator messageInterpolator; @Nullable private TraversableResolver traversableResolver; ... // 省略所有set方法(木有get方法) // 默认设置~~~~初始化 @Override public void afterPropertiesSet() { if (this.validatorFactory == null) { this.validatorFactory = Validation.buildDefaultValidatorFactory(); } // 这一句就是new ValidatorContextImpl( this ) ValidatorContext validatorContext = this.validatorFactory.usingContext(); // 插值器 MessageInterpolator targetInterpolator = this.messageInterpolator; if (targetInterpolator == null) { targetInterpolator = this.validatorFactory.getMessageInterpolator(); } validatorContext.messageInterpolator(new LocaleContextMessageInterpolator(targetInterpolator)); if (this.traversableResolver != null) { validatorContext.traversableResolver(this.traversableResolver); } // 把已经配置好的这个Validator设置进去~ setTargetValidator(validatorContext.getValidator()); } }
命名中就能可以看出,它是一个Bean,所以可以配合Spring容器一起使用。Spring内部虽然没有直接使用到它,但我们自己有需求的话自己可以使用它(其实更多的还是使用更强的子类)~
LocalValidatorFactoryBean
它和CustomValidatorBean平级,都是继承自SpringValidatorAdapter,但是它提供的能力更加的强大,比如Spring处理校验这块最重要的处理器MethodValidationPostProcessor就是依赖于它来给提供验证器~
它是Spring上下文中javax.validation的中心配置类。
// @since 3.0 这个类非常的丰富 实现了接口javax.validation.ValidatorFactory // 实现了ApplicationContextAware拿到Spring上下文... // 但其实,它的实际工作都是委托式,自己只提供了各式各样的配置~~~(主要是配置JSR) public class LocalValidatorFactoryBean extends SpringValidatorAdapter implements ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean { ... // 省略所有的配置属性 ... // 省略所有的get/set ... // 省略afterPropertiesSet()进行的默认配置初始化 最终调用setTargetValidator(this.validatorFactory.getValidator()); // 备注:还记得上文吗?上文的validator校验器是从上下文拿的,这里是从工厂拿的 // 省略所有对ValidatorFactory接口的方法实现~ }
这个类是非常重要的,虽然它也不被Spring直接使用,但是它是基石。
备注:虽然命名后缀是FactoryBean,但它并不是org.springframework.beans.factory.FactoryBean这个接口的子类。
其实这是断句问题,正确断句方式是:Local ValidatorFactory Bean~
OptionalValidatorFactoryBean
@since 4.0.1提供的,它做的唯一一件事:让org.springframework.validation.Validator成为可选(即使没有初始化成功,也不会报错,相当于把异常吃了嘛~)
// @since 4.0.1 public class OptionalValidatorFactoryBean extends LocalValidatorFactoryBean { @Override public void afterPropertiesSet() { try { super.afterPropertiesSet(); } catch (ValidationException ex) { LogFactory.getLog(getClass()).debug("Failed to set up a Bean Validation provider", ex); } } }
综上,若你想使用org.springframework.validation.SmartValidator来完成对Bean的校验,那就手动定义一个这样的Bean,然后自行调用API校验完成校验~
若你想这一切能面向注解编程,自动完成校验,那就听下文分解吧(也是最为关心,最为重要的内容)~
SpringConstraintValidatorFactory
ConstraintValidatorFactory整个API前问有讲过,本类就是Spring对它的扩展,从而和Spring容器整合了~
public class SpringConstraintValidatorFactory implements ConstraintValidatorFactory { private final AutowireCapableBeanFactory beanFactory; public SpringConstraintValidatorFactory(AutowireCapableBeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; } // 注意:此处是直接调用了create方法,放进容器 @Override public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { return this.beanFactory.createBean(key); } // Bean Validation 1.1 releaseInstance method public void releaseInstance(ConstraintValidator<?, ?> instance) { this.beanFactory.destroyBean(instance); } }
MessageSourceResourceBundleLocator
这个类也非常有意思,它扩展了Hibernate包的ResourceBundleLocator国际化,而使用
Spring自己的国际化资源:org.springframework.context.MessageSource
说明:ResourceBundleLocator是它Hibernate的一个SPI,Hibernate内部自己对它可是也有实现的哦~(Bean Validation内部大量的用到了SPI技术,有兴趣的可以了解)
public class MessageSourceResourceBundleLocator implements ResourceBundleLocator { private final MessageSource messageSource; public MessageSourceResourceBundleLocator(MessageSource messageSource) { Assert.notNull(messageSource, "MessageSource must not be null"); this.messageSource = messageSource; } @Override public ResourceBundle getResourceBundle(Locale locale) { return new MessageSourceResourceBundle(this.messageSource, locale); } }
关于MessageSourceResourceBundle它,就相对比较熟悉点了,它不是校验专用的,是Spring整体上用来处理国际化资源:MessageSource,java.util.ResourceBundl的帮助类~
//@since 27.02.2003 java.util.ResourceBundle 它是JDK提供来读取国际化的属性配置文件的 是个抽象类 public class MessageSourceResourceBundle extends ResourceBundle { private final MessageSource messageSource; private final Locale locale; public MessageSourceResourceBundle(MessageSource source, Locale locale) { Assert.notNull(source, "MessageSource must not be null"); this.messageSource = source; this.locale = locale; } public MessageSourceResourceBundle(MessageSource source, Locale locale, ResourceBundle parent) { this(source, locale); setParent(parent); } @Override @Nullable protected Object handleGetObject(String key) { try { return this.messageSource.getMessage(key, null, this.locale); } catch (NoSuchMessageException ex) { return null; } } // @since 1.6 @Override public boolean containsKey(String key) { try { this.messageSource.getMessage(key, null, this.locale); return true; } catch (NoSuchMessageException ex) { return false; } } @Override public Enumeration<String> getKeys() { throw new UnsupportedOperationException("MessageSourceResourceBundle does not support enumerating its keys"); } @Override public Locale getLocale() { return this.locale; } }
Spring环境下不仅可以使用Hibernate的国际化文件,也可以借助MessageSourceResourceBundleLocator搞自己的。