您的位置:首页 > 编程语言 > Java开发

Insight Springmvc's validator谈谈程序可扩展实现策略

2017-01-10 18:50 441 查看

使用场景

如果需要在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;
}
}

然后,LocalValidatorFactoryBean 会在初始化过程构造default-validatorFactory,得到hibernate-validator。

......

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());

4000
providers.put(cn, p);
return p;
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息