java bean 验证
这篇文章总结了一些简单,快速的示例,这些示例说明了您想使用Java Beans Validation API(JSR 349,JSR 303)进行的最常见操作。 记住,Beans Validation独立于Java EE。 尽管它是作为Java EE兼容服务器的一部分内置的,但该API也可以在Java SE应用程序中轻松使用。 所有这些示例都使用Java SE。
要求
我使用以下主要技术完成了本文的所有工作。 您可能可以使用不同的技术或版本来做相同的事情,但不能保证。
- Java 1.8.0_65_x64
- NetBeans 8.2
- Maven 3.0.5(与NetBeans捆绑在一起)
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.2.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
下载
访问我的GitHub页面https://github.com/mjremijan以查看我所有的开源项目。 这篇文章的代码位于: https : //github.com/mjremijan/thoth-beanvalidation
基本
此示例显示了使用内置的标准约束和内置的标准验证器进行Bean验证的基础。
清单1.1 –验证的Bean
package org.thoth.beanvalidation.basics;
import javax.validation.constraints.NotNull;
public class Widget {
@NotNull
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
清单1.2 –如何验证
package org.thoth.beanvalidation.basics;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class WidgetTest {
protected Validator validator;
@Before
public void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void violations_size() {
// setup
Widget w = new Widget();
// action
Set<ConstraintViolation<Widget>> violations
= validator.validate(w);
// assert
Assert.assertEquals(1, violations.size());
}
@Test
public void violation_message() {
// setup
Widget w = new Widget();
// action
Set<ConstraintViolation<Widget>> violations
= validator.validate(w);
// assert
ConstraintViolation<Widget> v
= violations.stream().findFirst().get();
Assert.assertEquals("may not be null", v.getMessage());
}
@Test
public void violation_messageTemplate() {
// setup
Widget w = new Widget();
// action
Set<ConstraintViolation<Widget>> violations
= validator.validate(w);
// assert
ConstraintViolation<Widget> v
= violations.stream().findFirst().get();
Assert.assertEquals("{javax.validation.constraints.NotNull.message}", v.getMessageTemplate());
}
@Test
public void violation_propertyPath() {
// setup
Widget w = new Widget();
// action
Set<ConstraintViolation<Widget>> violations
= validator.validate(w);
// assert
ConstraintViolation<Widget> v
= violations.stream().findFirst().get();
Assert.assertEquals("name", v.getPropertyPath().toString());
}
}
自定义消息模板
本示例说明如何使用自定义错误消息而不是使用内置标准错误消息来定制内置标准约束。
清单2.1 – ValidationMessages.properties
Candy.name.NotNull=A candy name is required.
清单2.2 –验证的Bean
package org.thoth.beanvalidation.custommessage;
import javax.validation.constraints.NotNull;
public class Candy {
@NotNull(message = "{Candy.name.NotNull}")
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
清单2.3 –如何验证
package org.thoth.beanvalidation.custommessage;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class CandyTest {
protected static Validator validator;
@BeforeClass
public static void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void notnull_violation_message() {
// setup
Candy candy = new Candy();
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
ConstraintViolation<Candy> v
= violations.stream().findFirst().get();
Assert.assertEquals("A candy name is required.", v.getMessage());
}
@Test
public void notnull_violation_messageTemplate() {
// setup
Candy candy = new Candy();
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
ConstraintViolation<Candy> v
= violations.stream().findFirst().get();
Assert.assertEquals("{Candy.name.NotNull}", v.getMessageTemplate());
}
}
具有可变替换的自定义消息模板
此示例说明了如何使用自定义错误消息配置内置的标准约束,该错误消息中的变量值在运行时由Bean验证替换。 可以替换的变量的示例包括已验证的实际值以及@Size
约束的min和max属性。
清单3.1 – ValidationMessages.properties
Candy.name.Size.message=The candy name "${validatedValue}" is invalid. It must be between {min} and {max} characters long
清单3.2 –验证的Bean
package org.thoth.beanvalidation.variablereplacement;
import javax.validation.constraints.Size;
public class Candy {
private String name;
@Size(message = "{Candy.name.Size.message}", min=5, max=10)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
清单3.3 –如何验证
package org.thoth.beanvalidation.variablereplacement;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.thoth.beanvalidation.variablereplacement.Candy;
public class CandyTest {
protected static Validator validator;
@BeforeClass
public static void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void does_the_constraint_have_the_correct_messageTemplate() {
// setup
Candy candy = new Candy();
candy.setName("");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
ConstraintViolation<Candy> v
= violations.stream().findFirst().get();
Assert.assertEquals("{Candy.name.Size.message}", v.getMessageTemplate());
}
@Test
public void is_the_message_correct_if_size_is_too_small() {
// setup
Candy candy = new Candy();
candy.setName("foo");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
ConstraintViolation<Candy> v
= violations.stream().findFirst().get();
Assert.assertEquals("The candy name \"foo\" is invalid. It must be between 5 and 10 characters long", v.getMessage());
}
@Test
public void is_the_message_correct_if_size_is_too_big() {
// setup
Candy candy = new Candy();
candy.setName("123456789|1");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
ConstraintViolation<Candy> v
= violations.stream().findFirst().get();
Assert.assertEquals("The candy name \"123456789|1\" is invalid. It must be between 5 and 10 characters long", v.getMessage());
}
}
自定义属性验证器
本示例说明如何为类的属性创建您自己的约束和您自己的验证器。
清单4.1 – ValidationMessages.properties
org.thoth.beanvalidation.propertyvalidator.Excludes.message=The value "${validatedValue}" is one of {value} which is forbidden.
清单4.2 –约束注释
package org.thoth.beanvalidation.propertyvalidator;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
@Target({
ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ExcludesValidator.class})
@Documented
public @interface Excludes {
String message() default "{org.thoth.beanvalidation.propertyvalidator.Excludes.message}";
Class[] groups() default {};
Class[] payload() default {};
String[] value() default {};
}
清单4.3 –约束验证器
package org.thoth.beanvalidation.propertyvalidator;
import java.util.Arrays;
import java.util.List;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ExcludesValidator
implements ConstraintValidator< Excludes, String> {
private List<String> excludeTheseValues;
@Override
public void initialize(Excludes arg) {
String[] strarr = arg.value();
if (strarr == null) {
strarr = new String[]{};
}
excludeTheseValues = Arrays.asList(strarr);
}
@Override
public boolean isValid(String value, ConstraintValidatorContext cvc) {
if (excludeTheseValues.contains(value)) {
return false;
} else {
return true;
}
}
}
清单4.4 –验证的Bean
package org.thoth.beanvalidation.propertyvalidator;
public class Candy {
private String name;
public Candy(String name) {
this.name = name;
}
@Excludes({"foo", "bar", "shrubbery"})
public String getName() {
return name;
}
}
清单4.5 –如何验证
package org.thoth.beanvalidation.propertyvalidator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
public class CandyTest {
protected static Validator validator;
@BeforeClass
public static void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void a_non_excludeded_name_should_not_give_you_a_constraint_violation() {
// setup
Candy candy = new Candy("hershey");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(candy);
// assert
assertEquals(0, violations.size());
}
@Test
public void do_you_get_a_constraint_violation_if_you_use_excluded_name_foo() {
// setup
Candy candy = new Candy("foo");
// action
ConstraintViolation<Candy> violation
= validator.validate(candy).iterator().next();
// assert
assertEquals("{org.thoth.beanvalidation.propertyvalidator.Excludes.message}", violation.getMessageTemplate());
assertEquals("The value \"foo\" is one of [foo, bar, shrubbery] which is forbidden.", violation.getMessage());
}
@Test
public void do_you_get_a_constraint_violation_if_you_use_excluded_name_bar() {
// setup
Candy candy = new Candy("bar");
// action
ConstraintViolation<Candy> violation
= validator.validate(candy).iterator().next();
// assert
assertEquals("{org.thoth.beanvalidation.propertyvalidator.Excludes.message}", violation.getMessageTemplate());
assertEquals("The value \"bar\" is one of [foo, bar, shrubbery] which is forbidden.", violation.getMessage());
}
@Test
public void do_you_get_a_constraint_violation_if_you_use_excluded_name_shrubbery() {
// setup
Candy candy = new Candy("shrubbery");
// action
ConstraintViolation<Candy> violation
= validator.validate(candy).iterator().next();
// assert
assertEquals("{org.thoth.beanvalidation.propertyvalidator.Excludes.message}", violation.getMessageTemplate());
assertEquals("The value \"shrubbery\" is one of [foo, bar, shrubbery] which is forbidden.", violation.getMessage());
}
}
自定义类验证器
此示例说明如何创建适用于整个类的约束和验证器。
清单5.1 – ValidationMessages.properties
org.thoth.beanvalidation.classvalidator.IdentificationExists.message=At least one of social security number, drivers license number, or passport number must exist.
清单5.2 –约束注释
package org.thoth.beanvalidation.classvalidator;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {IdentificationExistsValidator.class})
@Documented
public @interface IdentificationExists {
String message() default "{org.thoth.beanvalidation.classvalidator.IdentificationExists.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
清单5.3 –约束验证器
package org.thoth.beanvalidation.classvalidator;
import java.util.Objects;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IdentificationExistsValidator implements ConstraintValidator<IdentificationExists, Identification> {
@Override
public void initialize(IdentificationExists a) {}
@Override
public boolean isValid(Identification t, ConstraintValidatorContext cvc) {
boolean invalid =
Objects.equals(t.getDriversLicenseNumber(), null)
&&
Objects.equals(t.getPassportNumber(), null)
&&
Objects.equals(t.getSocialSecurityNumber(), null)
;
return !invalid;
}
}
清单5.4 –验证的Bean
package org.thoth.beanvalidation.classvalidator;
@IdentificationExists
public class Identification {
protected String socialSecurityNumber;
protected String driversLicenseNumber;
protected String passportNumber;
public String getSocialSecurityNumber() {
return socialSecurityNumber;
}
public void setSocialSecurityNumber(String socialSecurityNumber) {
this.socialSecurityNumber = socialSecurityNumber;
}
public String getDriversLicenseNumber() {
return driversLicenseNumber;
}
public void setDriversLicenseNumber(String driversLicenseNumber) {
this.driversLicenseNumber = driversLicenseNumber;
}
public String getPassportNumber() {
return passportNumber;
}
public void setPassportNumber(String passportNumber) {
this.passportNumber = passportNumber;
}
}
清单5.5 –如何验证
package org.thoth.beanvalidation.classvalidator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class IdentificationTest {
protected Validator validator;
@Before
public void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void violation_if_all_are_missing() {
// setup
Identification id = new Identification();
// action
Set<ConstraintViolation<Identification>> violations
= validator.validate(id);
// assert
ConstraintViolation<Identification> v
= violations.stream().findFirst().get();
Assert.assertEquals("At least one of social security number, drivers license number, or passport number must exist.", v.getMessage());
}
@Test
public void no_violation_if_social_security_number_exists() {
// setup
Identification id = new Identification();
id.setSocialSecurityNumber("a");
// action
Set<ConstraintViolation<Identification>> violations
= validator.validate(id);
// assert
Assert.assertEquals(0, violations.size());
}
@Test
public void no_violation_if_drivers_license_number_exists() {
// setup
Identification id = new Identification();
id.setDriversLicenseNumber("a");
// action
Set<ConstraintViolation<Identification>> violations
= validator.validate(id);
// assert
Assert.assertEquals(0, violations.size());
}
@Test
public void no_violation_if_passport_number_exists() {
// setup
Identification id = new Identification();
id.setPassportNumber("a");
// action
Set<ConstraintViolation<Identification>> violations
= validator.validate(id);
// assert
Assert.assertEquals(0, violations.size());
}
}
组序列(短路)
此示例显示了在进行验证时如何使用@GroupSequence
作为短路。 这意味着,如果第一轮验证未通过,则验证“短路”,并且不会执行第二轮验证。
默认情况下,所有bean验证约束都放入“默认”组序列中。 但是,通过将@GroupSequence
放在类上(如下所示),仅针对该类重新定义“默认”组序列。 使用下面的类上的@GroupSequence
,其基本作用是在bean验证期间,第一个操作是验证该类中未专门分配组的所有约束。 首先是@NotNull
约束。 如果所有这些都OK,那么第二个操作是验证Second.class
组中的所有约束。 那就是@Size
约束。 如果所有这些都OK,那么第三Third.class
操作将验证Third.class
组中的所有约束。 那就是@Pattern
约束。 如果某个小组在任何时候都无法通过验证,则验证将“短路”,并且验证不再进行。
清单6.1 –验证的Bean
package org.thoth.beanvalidation.groupsequence;
import javax.validation.GroupSequence;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@GroupSequence({Candy.class, Candy.Second.class, Candy.Third.class})
public class Candy {
protected interface Second {}
protected interface Third {}
private String name;
@NotNull()
@Size(min=4, max=10, groups = Second.class )
@Pattern(regexp = "[a-z]", groups = Third.class)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
清单6.2 –如何验证
package org.thoth.beanvalidation.groupsequence;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
public class CandyTest {
private Validator validator;
@Before
public void before() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void short_circuits_first_if_null() {
// setup
Candy w = new Candy();
// action
Set<ConstraintViolation<Candy>> violations
//= validator.validate(w, CheckGroupSequence.class);
= validator.validate(w);
// assert
assertEquals(1, violations.size());
assertEquals("may not be null", violations.iterator().next().getMessage());
}
@Test
public void short_circut_if_size_is_in_violation() {
// setup
Candy w = new Candy();
w.setName("foo");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(w);
// assert
assertEquals(1, violations.size());
assertEquals("size must be between 4 and 10", violations.iterator().next().getMessage());
}
@Test
public void short_circuit_if_pattern_is_in_violation() {
// setup
Candy w = new Candy();
w.setName("SHRUBBERY");
// action
Set<ConstraintViolation<Candy>> violations
= validator.validate(w);
// assert
assertEquals(1, violations.size());
assertEquals("must match \"[a-z]\"", violations.iterator().next().getMessage());
}
}
摘要
Bean验证是一种功能强大的API,尤其是因为它可以在Java EE服务器或独立的Java SE应用程序中使用。 这只是Bean验证API基础知识的简短摘要,但是通常,它足以涵盖开发人员有关如何使用它的大多数问题。
翻译自: https://www.javacodegeeks.com/2017/07/java-bean-validation-basics.html
java bean 验证