我正在尝试按照this link的答案中的示例为集合创建BV约束验证器。
public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {
private ValidatorContext validatorContext;
public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) {
this.validatorContext = nativeValidator;
}
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
T instance = null;
try {
instance = key.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) {
ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance;
validator.setValidatorContext(validatorContext);
}
return instance;
}
}
这是验证器。
public class CinCodeValidator implements ConstraintValidator<CinCode, String> {
private static Pattern cinCodePattern;
public void initialize(CinCode constraintAnnotation) {
if (cinCodePattern == null) {
cinCodePattern = Pattern.compile("([0-9]{1,5})");
}
}
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean result = false;
if (isNotNull(value)) {
result = matchCode(value);
}
if(!result) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( "Invalid Cin code" ).addConstraintViolation();
}
return result;
}
private static boolean isNotNull(Object obj) {
return obj != null;
}
private boolean matchCode(String value) {
Matcher matcher = cinCodePattern.matcher(value);
return matcher.matches();
}
}
public class CollectionElementBean {
private String cinCode;
@CinCode(message = "This should be a valid CIN code")
public String getCinCode() {
return cinCode;
}
public void setCinCode(String cinCode) {
this.cinCode = cinCode;
}
}
public interface ValidatorContextAwareConstraintValidator {
void setValidatorContext(ValidatorContext validatorContext);
}
收集验证器:
public class ValidCollectionValidator implements ConstraintValidator<ValidCollection, Collection>, ValidatorContextAwareConstraintValidator {
private static final Logger logger = LoggerFactory.getLogger(ValidCollectionValidator.class);
private ValidatorContext validatorContext;
private Class<?> elementType;
private Class<?>[] constraints;
private boolean allViolationMessages;
public void setValidatorContext(ValidatorContext validatorContext) {
this.validatorContext = validatorContext;
}
public void initialize(ValidCollection constraintAnnotation) {
elementType = constraintAnnotation.elementType();
constraints = constraintAnnotation.constraints();
allViolationMessages = constraintAnnotation.allViolationMessages();
}
public boolean isValid(Collection collection, ConstraintValidatorContext context) {
boolean valid = true;
if (collection == null) {
return false;
}
Validator validator = validatorContext.getValidator();
boolean beanConstrained = validator.getConstraintsForClass(elementType).isBeanConstrained();
for (Object element : collection) {
Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
if (beanConstrained) {
boolean hasValidCollectionConstraint = hasValidCollectionConstraint(elementType);
if (hasValidCollectionConstraint) {
// elementType has @ValidCollection constraint
violations.addAll(validator.validate(element));
} else {
violations.addAll(validator.validate(element));
}
} else {
for (Class<?> constraint : constraints) {
String propertyName = constraint.getSimpleName();
propertyName = Introspector.decapitalize(propertyName);
violations.addAll(validator.validateValue(CollectionElementBean.class, propertyName, element)); // Here, only failed values get added.
}
}
if (!violations.isEmpty()) {
valid = false;
}
if (allViolationMessages) {
for (ConstraintViolation<?> violation : violations) {
logger.debug(violation.getMessage());
ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(violation.getMessage());
violationBuilder.addConstraintViolation();
}
}
}
return valid;
}
private boolean hasValidCollectionConstraint(Class<?> beanType) {
BeanDescriptor beanDescriptor = validatorContext.getValidator().getConstraintsForClass(beanType);
boolean isBeanConstrained = beanDescriptor.isBeanConstrained();
if (!isBeanConstrained) {
return false;
}
Set<ConstraintDescriptor<?>> constraintDescriptors = beanDescriptor.getConstraintDescriptors();
for (ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
if (constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
return true;
}
}
Set<PropertyDescriptor> propertyDescriptors = beanDescriptor.getConstrainedProperties();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
for (ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
if (constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
return true;
}
}
}
return false;
}
}
带有GEO Id列表,州号和CIN的表格:
public class FormWithCollection {
private List<String> cinCodes;
@NotNull
@ValidCollection(elementType = String.class, constraints = { CinCode.class })
public List<String> getCinCodes() {
return cinCodes;
}
public void setCinCodes(List<String> cinCodes) {
this.cinCodes = cinCodes;
}
//Same goes for GEO Id and State number
}
测试程序:
public class ValidCollectionTest {
private ValidatorFactory validatorFactory;
@Before
public void createValidatorFactory() {
validatorFactory = Validation.buildDefaultValidatorFactory();
}
private Validator getValidator() {
ValidatorContext validatorContext = validatorFactory.usingContext();
validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext));
Validator validator = validatorContext.getValidator();
return validator;
}
/**
A valid CIN code is 1 to 5 digit numeric.
*/
@Test
public void validateCollectionWithInvalidCIN() {
FormWithCollection formWithCollection = new FormWithCollection();
formWithCollection.setCinCode(Arrays.asList("12345", "111a1"));
Validator validator = getValidator();
Set<ConstraintViolation<FormWithCollection>> violations = validator.validate(formWithCollection, Default.class);
for (ConstraintViolation<FormWithCollection> violation : violations) {
System.out.println(violation.getMessage() + "\t" + violation.getInvalidValue());
}
Assert.assertEquals(1, violations.size()); // I expect to see just 1 violation as there is only one invalid value "111a1". But 2 violations are returned.
}
}
在ValidCollectionTest.java中,迭代Set of ConstraintViolation以列出所有违规。我试图列出每个violation.getInvalidValue()让用户知道。但getInvalidValue()仅返回整个集合而不是失败的值。
我想向用户显示无效值,因为我有一个这样的表单:
+-----------+----------+---------+
|Geo ID |State # |CIN |
+-----------+----------+---------+
| | | |
+-----------+----------+---------+
| | | |
+-----------+----------+---------+
| | | |
+-----------+----------+---------+
其中GEO Id,州号和CIN是三种不同的输入格式。
此问题是否有解决方法只能登记失败的值?
答案 0 :(得分:0)
我认为您所指的帖子中的解决方案不是一个好主意。您是否需要针对一个特定约束进行验证?如果是这样,只需创建一个ConstraintValidator<MyConstraint, Collection>
。迭代传递的集合并自己验证每个元素。它还有助于显示您拥有的代码(约束,约束验证器等)。或者如果您使用的是Java 8,您可以尝试使用最新的Hibernate Validator 5.2版本,它允许您使用类型注释,例如List<@MyConstraint String>
。