java ee7 -- Java Bean验证

时间:2022-01-09 15:14:53

针对对象、对象成员、方法、构造函数的数据验证。

1. 一个验证的小例子

(1) 添加引用jar

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.7.Final</version>
</dependency> <dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b09</version>
</dependency>

(2) 编写工具类

public static <T> void validate(T t) throws ValidationException {
ValidatorFactory vFactory = Validation.buildDefaultValidatorFactory();
Validator validator = vFactory.getValidator(); Set<ConstraintViolation<T>> set = validator.validate(t);
if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage());
}
throw new ValidationException(validateError.toString());
}
}

(3) 编写bean

package beans;

import javax.validation.constraints.NotNull;

public class Person {
@NotNull(message="用户名不能为空")
private String username; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
}
}

(4) 编写测试类

package init;

import javax.xml.bind.ValidationException;

import beans.Person;
import validation.ValidationUtil; public class Test {
public static void main(String [] args ){
Person person = new Person();
try {
ValidationUtil.validate(person);
} catch (ValidationException e) {
System.out.println(e.getMessage()); //输出结果是:用户名不能为空
} }
}

(5)输出结果

用户名不能为空

2. 内置bean验证约束

(1)@AsserFalse
字段的值必须是false.
(2)@AsserTrue
字段的值必须是true.
(3)@DecimalMax
字段的值必须是一个小数值,应小于或者等于value元素中的数
(4)@DecimalMin
字段的值必须是一个小数值,应该大于或者等于value元素中的数
(5)@Digits
字段必须是指定范围内的一个数,integer元素指定了这个数的最大小整数位数,ftaction元素指定这个数的最大小数
(6)@Future
字段的值必须是将来一个日期
(7)@Max
字段必须是一个整数类型,应该小于或者等于value的数
(8)@Min
字段必须一个整数类型,应该大于或者等于value的数
(9)NotNull
字段的值必须不能为空
(10)@Null
字段的值必须为空
(11)@Past
字段的值必须是过去的一个日期
(12)@Pattern
字段的值必须与正则表达式匹配

(13)@Size

字段的大小,必须指定在一个范围匹配

package beans.validation;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map; import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size; public class BuiltInValidationBean {
    @AssertTrue(message="isActive    @AssertTrue    值应该为true")
    private boolean isActive;
    
    @AssertFalse(message="isUnsupported    @AssertFalse    值应该为false")
    private boolean isUnsupported;
    
    @Max(value=100, message="quantityMax    @Max    最大值是100")
    private int quantityMax;
    
    @Min(value=10, message="quantityMin    @Min    最小值是10")
    private int quantityMin;
    
    @DecimalMax(value="100", message="discountMax    @DecimalMax 最大值是100")
    private BigDecimal discountMax;
    
    @DecimalMin(value="10", message="discountMin    @DecimalMin 最小值是10")
    private BigDecimal discountMin;     @Digits(integer=3, fraction=1, message="price    @Digits    最大整数位数是3, 最大小数位数是1")
    private BigDecimal price;
    
    @Future(message="eventDate    @Future    字段值必须是一个将来的日期")
    private Date eventDate;
    
    @Past(message="birthday    @Past    字段值必须是一个过去的日期")
    private Date birthday;     @NotNull(message="username    @NotNull    username不能为空")
    private String username;  
    
    @Null(message="unusedString    @Null    unusedString必须为空")
    private String unusedString;  
    
    @Pattern(regexp="\\(\\d(3)\\)\\d{9}", message="phoneNumber    @Pattern    手机号码不正确")
    private String phoneNumber;
    
    @Size(min=2, max=8, message="briefMessage    @Size    字符串最短2,最长8")
    private String briefMessage;
    
    @Size(min=2, max=8, message="briefList    @Size    list最短2,最长8")
    private List<String> briefList;
    
    @Size(min=2, max=8, message="briefMap    @Size    map最短2,最长8")
    private Map<String, String> briefMap;     public boolean isActive() {
        return isActive;
    }     public void setActive(boolean isActive) {
        this.isActive = isActive;
    }     public boolean isUnsupported() {
        return isUnsupported;
    }     public void setUnsupported(boolean isUnsupported) {
        this.isUnsupported = isUnsupported;
    }     public int getQuantityMax() {
        return quantityMax;
    }     public void setQuantityMax(int quantityMax) {
        this.quantityMax = quantityMax;
    }     public int getQuantityMin() {
        return quantityMin;
    }     public void setQuantityMin(int quantityMin) {
        this.quantityMin = quantityMin;
    }     public BigDecimal getDiscountMax() {
        return discountMax;
    }     public void setDiscountMax(BigDecimal discountMax) {
        this.discountMax = discountMax;
    }     public BigDecimal getDiscountMin() {
        return discountMin;
    }     public void setDiscountMin(BigDecimal discountMin) {
        this.discountMin = discountMin;
    }     public BigDecimal getPrice() {
        return price;
    }     public void setPrice(BigDecimal price) {
        this.price = price;
    }     public Date getEventDate() {
        return eventDate;
    }     public void setEventDate(Date eventDate) {
        this.eventDate = eventDate;
    }     public Date getBirthday() {
        return birthday;
    }     public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }     public String getUsername() {
        return username;
    }     public void setUsername(String username) {
        this.username = username;
    }     public String getUnusedString() {
        return unusedString;
    }     public void setUnusedString(String unusedString) {
        this.unusedString = unusedString;
    }     public String getPhoneNumber() {
        return phoneNumber;
    }     public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }     public String getBriefMessage() {
        return briefMessage;
    }     public void setBriefMessage(String briefMessage) {
        this.briefMessage = briefMessage;
    }     public List<String> getBriefList() {
        return briefList;
    }     public void setBriefList(List<String> briefList) {
        this.briefList = briefList;
    }     public Map<String, String> getBriefMap() {
        return briefMap;
    }     public void setBriefMap(Map<String, String> briefMap) {
        this.briefMap = briefMap;
    }
}

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

Validates all constraints on object -- 验证所有字段

	static ValidatorFactory vFactory = Validation.buildDefaultValidatorFactory();
static Validator validator = vFactory.getValidator();
static ExecutableValidator executableValidator = validator.forExecutables(); public static <T> void validate(T object, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<T>> set = validator.validate(object, groups); if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validate:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
}

        BuiltInValidationBean builtInValidationBean = new BuiltInValidationBean();
        
        builtInValidationBean.setActive(false);
        builtInValidationBean.setUnsupported(true);
        
        builtInValidationBean.setQuantityMax(101);
        builtInValidationBean.setQuantityMin(9);
        
        builtInValidationBean.setDiscountMax(new BigDecimal("100.1"));
        builtInValidationBean.setDiscountMin(new BigDecimal("9.9"));
        
        builtInValidationBean.setPrice(new BigDecimal("9999"));
        
        builtInValidationBean.setEventDate(new Date(1999, 12, 31));
        builtInValidationBean.setBirthday(new Date(2999, 12, 31));
        
        builtInValidationBean.setUsername(null);
        builtInValidationBean.setUnusedString("test");
        
        builtInValidationBean.setPhoneNumber("");
        
        builtInValidationBean.setBriefMessage("");
        builtInValidationBean.setBriefList(new ArrayList<>(20));
        builtInValidationBean.setBriefMap(new HashMap<>(20));
        
        try {  
            ValidationUtil.validate(builtInValidationBean);  
        } catch (ValidationException e) {  
            System.out.println("builtInValidationTest--validate");
            System.out.println(e.getMessage());
        }

输出结果

builtInValidationTest--validate
username    @NotNull    username不能为空
briefMessage    @Size    字符串最短2,最长8
quantityMin    @Min    最小值是10
isUnsupported    @AssertFalse    值应该为false
unusedString    @Null    unusedString必须为空
discountMax    @DecimalMax 最大值是100
price    @Digits    最大整数位数是3, 最大小数位数是1
discountMin    @DecimalMin 最小值是10
isActive    @AssertTrue    值应该为true
quantityMax    @Max    最大值是100
birthday    @Past    字段值必须是一个过去的日期
briefList    @Size    list最短2,最长8
phoneNumber    @Pattern    手机号码不正确
briefMap    @Size    map最短2,最长8

Validates all constraints placed on the property of object named propertyName -- 验证指定字段

	public static <T> void validateProperty(T object, String propertyName, Class<?>... groups) throws ValidationException {
ValidatorFactory vFactory = Validation.buildDefaultValidatorFactory();
Validator validator = vFactory.getValidator(); Set<ConstraintViolation<T>> set = validator.validateProperty(object, propertyName, groups);
if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validateProperty:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
}

        builtInValidationBean = new BuiltInValidationBean();
        builtInValidationBean.setBriefMessage("");
        
        try {  
            ValidationUtil.validateProperty(builtInValidationBean, "briefMessage");  
        } catch (ValidationException e) {  
            System.out.println("builtInValidationTest--validateProperty");
            System.out.println(e.getMessage());
        }

输出结果

builtInValidationTest--validateProperty
briefMessage    @Size    字符串最短2,最长8

正则表达式全部符号解释

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
(pattern) 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
x|y 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\cx 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。.
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。

3. 验证Null和空串

允许JSF将空串当作null处理

<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>

如果某个元素有@NotNull约束, 且以上为true,则会传入null到bean验证, 会使@NotNull约束失败。

4. 验证构造函数

针对构造函数的参数设置bean约束;

package beans.validation;

import java.math.BigDecimal;

import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull; public class ConstructorValidationBean {
private String name; private BigDecimal salary; private String currency; public ConstructorValidationBean(
@NotNull(message="name不能为空") String name,
@NotNull(message="salary不能为空")
@Digits(integer=6, fraction=2, message="工资最大整数位数是6, 最大小数位数是2") BigDecimal salary,
@NotNull(message="currency不能为空") String currency) {
this.name = name;
this.salary = salary;
this.currency = currency;
} @NotNull(message="name/salary 不能为空") //跨参数约束, 应用于构造函数中所有参数
public ConstructorValidationBean(String name, BigDecimal salary) {
this.name = name;
this.salary = salary;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public BigDecimal getSalary() {
return salary;
} public void setSalary(BigDecimal salary) {
this.salary = salary;
} public String getCurrency() {
return currency;
} public void setCurrency(String currency) {
this.currency = currency;
} }

	public static <T> void validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<T>> set = executableValidator.validateConstructorParameters(constructor, parameterValues, groups);
if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validateConstructorParameters:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
} public static <T> void validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<T>> set = executableValidator.validateConstructorReturnValue(constructor, createdObject, groups);
if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validateConstructorReturnValue:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
}

	@Test
public void constructorValidationBeanTest() throws Exception {
Constructor<? extends ConstructorValidationBean> constructor = ConstructorValidationBean.class.getConstructor(String.class, BigDecimal.class, String.class);
Object[] parameterValues = { null, new BigDecimal("8888888.88"),null }; try {
ValidationUtil.validateConstructorParameters(constructor, parameterValues);
} catch (ValidationException e) {
System.out.println("constructorValidationBean--validateConstructorParameters");
System.out.println(e.getMessage());
} constructor = ConstructorValidationBean.class.getConstructor(String.class, BigDecimal.class);
parameterValues = new Object[]{null, new BigDecimal("8888888.88")};
try {
ValidationUtil.validateConstructorReturnValue(constructor, parameterValues);
} catch (ValidationException e) {
System.out.println("constructorValidationBean--validateConstructorParameters--跨参数约束");
System.out.println(e.getMessage());
} constructor = ConstructorValidationBean.class.getConstructor(String.class, BigDecimal.class);
try {
ValidationUtil.validateConstructorReturnValue(constructor, new ConstructorValidationBean(null, new BigDecimal("8888888.88")));
} catch (ValidationException e) {
System.out.println("constructorValidationBean--validateConstructorReturnValue");
System.out.println(e.getMessage());
}

输出结果

constructorValidationBean--validateConstructorParameters
validateConstructorParameters:
name不能为空
currency不能为空
工资最大整数位数是6, 最大小数位数是2

5. 验证方法

针对非静态方法的参数以及非静态方法的返回值设置bean约束;

package beans.validation;

import java.util.ArrayList;
import java.util.List; import javax.validation.constraints.Max;
import javax.validation.constraints.Size; public class MethodValidationBean {
public void drive(@Max(80) int speedInMph) {
// ...
} @Size(min = 3, max = 8)
public List<String> getPassengers() {
return new ArrayList<>();
}
}
	public static <T> void validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<T>> set = executableValidator.validateParameters(object, method, parameterValues, groups); if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validateParameters:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
} public static <T> void validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<T>> set = executableValidator.validateReturnValue(object, method, returnValue, groups); if (set.size() > 0) {
StringBuilder validateError = new StringBuilder();
validateError.append("validateReturnValue:\r\n"); for (ConstraintViolation<T> val : set) {
validateError.append(val.getMessage()).append("\r\n");
} throw new ValidationException(validateError.toString());
}
}
	@Test
public void methodValidationBeanTest() throws Exception {
MethodValidationBean methodValidationBean = new MethodValidationBean();
Method method = MethodValidationBean.class.getMethod("drive", int.class);
Object[] parameterValues = { 81 }; try {
ValidationUtil.validateParameters(methodValidationBean, method, parameterValues);
} catch (ValidationException e) {
System.out.println("methodValidationBeanTest--validateParameters");
System.out.println(e.getMessage());
} method = MethodValidationBean.class.getMethod("getPassengers");
Object returnValue = Collections.emptyList(); try {
ValidationUtil.validateReturnValue(methodValidationBean, method, returnValue);
} catch (ValidationException e) {
System.out.println("methodValidationBeanTest--validateReturnValue");
System.out.println(e.getMessage());
}
}

执行结果

methodValidationBeanTest--validateParameters
validateParameters:
最大不能超过80

methodValidationBeanTest--validateReturnValue
validateReturnValue:
个数必须在3和8之间

6. 自定义约束

package beans.validation.annotation;

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.ConstraintTarget;
import javax.validation.Payload;
import javax.validation.constraints.Pattern; import validation.EmailValidator; @Pattern.List({
    @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+"+"(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"+"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
})
@Constraint(validatedBy={EmailValidator.class})
@Documented
@Target({
    ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
public @interface Email {
    String message() default "invalid email";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String[] value() default {};
    
    //设置约束
//    ConstraintTarget validationAppliesTo() default ConstraintTarget.PARAMETERS;
    
    
    @Target({
        ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER
    })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List{
        Email[] value();
    }
}
package validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import beans.validation.annotation.Email; public class EmailValidator implements ConstraintValidator<Email, String> { @Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println(value); return true;
} }
package beans.validation;

import beans.validation.annotation.Email;

public class EmailBean {
@Email
private String email; public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
}
}
    @Test
    public void selfDefineValidationBeanTest() {
        EmailBean emailBean = new EmailBean();
        emailBean.setEmail("email");
        
        try {
            ValidationUtil.validate(emailBean);
        } catch (ValidationException e) {  
            System.out.println("selfDefineValidationBeanTest--validate");
            System.out.println(e.getMessage());
        }
    }

执行结果
email
selfDefineValidationBeanTest--validate
validate:
需要匹配正则表达式"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"

去除约束目标的二义性

ConstraintTarget validationAppliesTo() default ConstraintTarget.PARAMETERS;

???

http://blog.****.net/java_1111/article/details/22603731

https://www.ibm.com/support/knowledgecenter/zh/SSEQTP_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/cdat_beanvaljpa.html

7. 组合约束

(1) 验证组

约束组由接口表示,约束组也可由其它组继承(到时候也会验证超类中的所有约束)

interface Employee{}

interface Manager extends Employee{}

interface Contractor{}

指定约束组

package beans.validation;

import javax.validation.constraints.NotNull;

public class ConstraintGroupBean {
    @NotNull(groups={Employee.class, Contractor.class})
    private String workPhone;     @NotNull(groups=Contractor.class)
    private String homePhone;     public String getWorkPhone() {
        return workPhone;
    }     public void setWorkPhone(String workPhone) {
        this.workPhone = workPhone;
    }     public String getHomePhone() {
        return homePhone;
    }     public void setHomePhone(String homePhone) {
        this.homePhone = homePhone;
    }
}

缺省情况下,如果在单个约束上没有指定验证组或多个组,那么使用 javax.validation.groups.Default 组来予以验证

(2) 指定验证顺序 GroupSequence

GroupSequence中第一个验证过了,才会验证第二个

https://www.jianshu.com/p/cf51061de60c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes

https://*.com/questions/30359124/java-groupsequenceprovider-for-validation-object-is-null-in-getvalidationgroup#

8. 使用 META-INF/validation.xml

https://docs.oracle.com/javaee/6/api/javax/validation/Configuration.html