使用场景
如果需要在controller 校验bean,需要
1.引入hibernate-validator 库;
2.Class 属性添加@NotNull、@Pattern、@Size限制条件;
3.controller 方法涉及到该Class入参添加@Valid,即可达到入参校验。
......
JSR-303
按照以往的惯例,spring 引入外部的支持,需要xml 进行对应的配置。hibernate-validator引入不用任何配置,就可以直接使用,是怎样的机制?
首先,Springmvc bean-validation遵循JSR-303标准。策略:如果Springmvc 不配置validator,则根据当前环境存在JSR-303 api决定是否构造validator。
// 检测当前classpath是否存在 validation-api
private static final boolean javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
if (element.hasAttribute("validator")) {
// 配置指定的validator
return new RuntimeBeanReference(element.getAttribute("validator"));
} else if (javaxValidationPresent) {
// 当前classpath 有可用的JSR-303 实现,关键!!!
RootBeanDefinition validatorDef = new RootBeanDefinition("org.springframework.validation.beanvalidation.LocalValidatorFactoryBean");
//...
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
return new RuntimeBeanReference(validatorName);
} else {
// 默认无可用的validator
return null;
}
}
......
java.util.ServiceLoader
如何得到Validator 接口的实现类,或者说扫描当前classpath,加载默认的实现类。这个就依赖jdk提供的ServiceLoader,功能类似bean-factory.getBean(name, requiredType)。
/**
* 构造ServiceLoader,find符合指定接口的实现类
*/
private List
> loadProviders(ClassLoader classloader) {
ServiceLoader
loader = ServiceLoader.load(ValidationProvider.class, classloader);
Iterator
providerIterator = loader.iterator();
List
> validationProviderList = new ArrayList
>(); while (providerIterator.hasNext()) { try { validationProviderList.add(providerIterator.next()); } catch (ServiceConfigurationError e) { // ... } } return validationProviderList; }
总结
程序的可扩展性实现方案:
1.程序本身已支持插件库的应用,根据运行环境,确定是否启用,类似springmvc启用默认校验的实现;
2.程序本身支持标准的api,利用ServiceLoader实现插件库的替换,类似hibernate-validator的集成。
......
More
Insight ServiceLoader look-up指定接口的Implement 实现
// service provider(Implement) 必须配置在指定的路径下,才会被遍历!
private static final String PREFIX = "META-INF/services/";
public boolean hasNext() {
try {
// fullname = META-INF/services/ + 指定接口的全名
String fullName = PREFIX + service.getName();
// 当前loader加载扫描到的文件,解析文件得到Implement's name
configs = loader.getResources(fullName);
pending = parse(service, configs.nextElement());
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
nextName = pending.next();
return true;
}
public S next() {
String cn = nextName;
try {
// 根据上一步解析到的Implement's name,得到Implement's instance
S p = service.cast(Class.forName(cn, true, loader).newInstance());
providers.put(cn, p);
return p;
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
}
}