有太多问题要回答,但我会尝试。
动态创建的约束映射
如示例所述,它是可用的。有两种方法可以创建自定义映射。
以编程方式创建自定义约束映射
假设有一个 POJO
import java.util.Map;
public class Foo {
private Map<String, String> rules;
public Map<String, String> getRules() {
return rules;
}
public void setRules(Map<String, String> rules) {
this.rules = rules;
}
}
描述的约束是:
- 地图不能为空
- 最大。地图大小为 2
- 地图的键至少应为 3 个字符
- 值不能为空并且
- 值必须为最大值。 5 个字符
像这样:
@NotNull
@Size(max=3)
private Map<@Size(min=3)String, @NotBlank @Size(max=5)String> rules;
约束映射为:
var configuration = Validation
.byProvider(HibernateValidator.class)
.configure();
ConstraintMapping constraintMapping = configuration.createConstraintMapping();
constraintMapping
.type(Foo.class)
.field("rules")
.constraint(new NotNullDef())
.constraint(new SizeDef().max(2))
.containerElementType(0)
.constraint(new SizeDef().min(3))
.containerElementType(1)
.constraint(new NotBlankDef())
.constraint(new SizeDef().max(5))
;
var validator = configuration.addMapping(constraintMapping)
.buildValidatorFactory()
.getValidator();
工作示例是:
public class FooValidatorTest {
@Test
void name() {
var configuration = Validation
.byProvider(HibernateValidator.class)
.configure();
ConstraintMapping constraintMapping = configuration.createConstraintMapping();
constraintMapping
.type(Foo.class)
.field("rules")
.constraint(new NotNullDef())
.constraint(new SizeDef().max(2))
.containerElementType(0)
.constraint(new SizeDef().min(3))
.containerElementType(1)
.constraint(new NotBlankDef())
.constraint(new SizeDef().max(5))
;
var validator = configuration.addMapping(constraintMapping)
.buildValidatorFactory()
.getValidator();
assertAll(
() -> {
var obj = new Foo();
/*
expected violation(s)
map is null
*/
var result = validator.validate(obj).stream().toList();
assertAll("Only @NotNull should violated",
() -> assertEquals(1, result.size()),
() -> assertEquals("{jakarta.validation.constraints.NotNull.message}",
result.get(0).getMessageTemplate()
)
);
},
() -> {
var obj = new Foo();
/*
expected violation(s)
map contains too much elements (max: 2)
note: keys and values are not validated
*/
obj.setRules(Map.of("name1", "desc1", "name2", "desc2", "name3", "desc3"));
var result = validator.validate(obj).stream().toList();
assertAll("Only @Size should violated",
() -> assertEquals(1, result.size()),
() -> assertEquals("{jakarta.validation.constraints.Size.message}",
result.get(0).getMessageTemplate()
)
);
},
() -> {
var obj = new Foo();
/*
expected violation(s)
first key is too short (min: 3)
first value is blank
second value is too long (max:5)
*/
obj.setRules(Map.of("aa", "", "bbb", "longValue"));
var result = validator.validate(obj).stream().toList();
var templates = result.stream().map(ConstraintViolation::getMessageTemplate).toList();
var expectedTemplates = new String[]{
"{jakarta.validation.constraints.NotBlank.message}",
"{jakarta.validation.constraints.Size.message}",
"{jakarta.validation.constraints.Size.message}"
};
assertAll(
() -> assertEquals(3, result.size()),
() -> assertThat(templates, containsInAnyOrder(expectedTemplates))
);
}
);
}
}
使用 xml 创建自定义约束映射
.addMapping 方法有另一个接受输入流的签名。
xml定义为:
<?xml version="1.0" encoding="UTF-8"?>
<constraint-mappings
xmlns="https://jakarta.ee/xml/ns/validation/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"https://jakarta.ee/xml/ns/validation/mapping https://jakarta.ee/xml/ns/validation/validation-mapping-3.0.xsd"
version="3.0">
<bean class="Foo">
<field name="rules">
<container-element-type type-argument-index="0">
<container-element-type>
<constraint annotation="jakarta.validation.constraints.Size">
<element name="min">3</element>
</constraint>
</container-element-type>
</container-element-type>
<container-element-type type-argument-index="1">
<container-element-type>
<constraint annotation="jakarta.validation.constraints.NotBlank"/>
<constraint annotation="jakarta.validation.constraints.Size">
<element name="max">5</element>
</constraint>
</container-element-type>
</container-element-type>
<constraint annotation="jakarta.validation.constraints.NotNull"/>
<constraint annotation="jakarta.validation.constraints.Size">
<element name="max">2</element>
</constraint>
</field>
</bean>
</constraint-mappings>
而且这个修改是必要的:
var validator = configuration
.addMapping(
getClass().getClassLoader()
.getResourceAsStream("foo_mapping.xml")
)
.buildValidatorFactory()
.getValidator();
加载外部类
在运行时字节码生成完成后,这些类应该加载并且加载的类应该传递给validator。
所以首先要做的是创建一个类加载器(ucl)。之后可以传递给HibernateValidatorConfiguration
var configuration = Validation
.byProvider(HibernateValidator.class)
.configure()
.externalClassLoader(ucl);
从技术上讲,它可以动态创建和加载 POJO 并运行自定义验证映射,但我认为这不是一个好方法。
每次创建类或创建自定义映射时,都应该创建一个新的 Validator 实例。虽然实例化一个新的验证器很便宜,但它需要一个新的ValidatorFactory,这非常昂贵(~400ms)。