欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习


1 文章概述

javax.validation是基于JSR-303标准定义的一组接口,目的是使开发者简洁地校验参数,hibernate-validator实现了这一组接口,可以作为工具独立引用。

<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>6.0.18.Final</version>
</dependency>

如果是SpringBoot项目则无需显示引用上述依赖,因为SpringBoot已经将上述依赖进行了集成。


2 基本使用

2.1 定义模型

import java.math.BigDecimal;
import java.util.List;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import lombok.Data;

@Data
public class OrderModelA {

    @Min(value = 1, message = "订单编号必须大于等于1")
    @Max(value = 100, message = "订单编号必须小于等于100")
    private Integer orderId;

    @NotBlank(message = "订单名称不能为空")
    private String orderName;

    @Size(min = 1, max = 10)
    private List<String> goodsList;

    @DecimalMin(value = "1", message = "订单金额必须大于等于1")
    private BigDecimal amount;
}

2.2 定义接口

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import com.java.front.validation.model.OrderModelA;

@Validated
public interface BizValidateService {

    void bizMethodA(@Valid OrderModelA model, @NotNull String param);
}

2.3 接口实现

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.java.front.validation.model.OrderModelA;

@Component
public class BizValidateServiceImpl implements BizValidateService {

    @Override
    public void bizMethodA(@Valid OrderModelA model, @NotNull String param) {
        System.out.println("execute bizA model=" + JSON.toJSONString(model) + ",param=" + param);
    }
}

2.4 测试用例

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAuthApplication {

    @Autowired
    private BizValidateService bizValidateService;

    @Test
    public void testBizValidateA_correct() {
        OrderModelA model = new OrderModelA();
        model.setOrderId(1);
        model.setOrderName("订单名称");
        List<String> goodsList = new ArrayList<String>();
        goodsList.add("goods1");
        goodsList.add("goods2");
        goodsList.add("goods3");
        model.setGoodsList(goodsList);
        model.setAmount(new BigDecimal("10"));
        bizValidateService.bizMethodA(model, "param");
    }

    @Test
    public void testBizValidateA_error() {
        OrderModelA model = new OrderModelA();
        model.setOrderId(0);
        model.setOrderName("订单名称");
        List<String> goodsList = new ArrayList<String>();
        goodsList.add("goods1");
        goodsList.add("goods2");
        goodsList.add("goods3");
        model.setGoodsList(goodsList);
        model.setAmount(new BigDecimal("10"));
        bizValidateService.bizMethodA(model, "param");
    }
}

2.5 报错信息

javax.validation.ConstraintViolationException: bizMethodA.model.orderId: 订单编号必须大于等于1
	at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:117)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	at com.java.front.validation.BizValidateServiceImpl$$EnhancerBySpringCGLIB$$42c25c43.bizMethodA(<generated>)
	at com.java.front.TestAuthApplication.testBizValidateA_error(TestAuthApplication.java:62)

3 复杂应用

现在我们假设一种场景,订单新增了type1、type2两个字段,这两个字段影响对于orderId值范围判断,也就是说orderId范围判断不再是静态的,而是受其它字段影响。

针对这种情况第一步我们可以构造type1、type2、orderId组合字段,第二步自定义校验器将组合字段拆开进行业务校验。


3.1 定义注解

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;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD })
@Constraint(validatedBy = TypeAndOrderIdValidator.class)
public @interface TypeAndOrderIdValid {

    String message() default "不满足业务条件";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

3.2 定义校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.java.front.validation.model.OrderModelB;

public class TypeAndOrderIdValidator implements ConstraintValidator<TypeAndOrderIdValid, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        String[] array = value.split(OrderModelB.JOINT);
        int type1 = Integer.parseInt(array[0]);
        int type2 = Integer.parseInt(array[1]);
        int orderId = Integer.parseInt(array[2]);
        if (type1 == 1 && type2 == 2) {
            return orderId > 20;
        }
        return true;
    }
}

3.3 定义模型

import java.math.BigDecimal;
import java.util.List;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.alibaba.fastjson.annotation.JSONField;
import com.java.front.server.validation.extend.TypeAndOrderIdValid;
import lombok.Data;

@Data
public class OrderModelB {
    public static final String JOINT = "_";

    @Min(value = 1, message = "订单编号必须大于等于1")
    @Max(value = 100, message = "订单编号必须小于等于100")
    private Integer orderId;

    @NotBlank(message = "订单名称不能为空")
    private String orderName;

    @Size(min = 1, max = 10)
    private List<String> goodsList;

    @DecimalMin(value = "1", message = "订单金额必须大于等于1")
    private BigDecimal amount;

    private int type1;

    private int type2;

    @JSONField(serialize = false)
    private String typeAndOrderIdComposite;

    @TypeAndOrderIdValid
    public String getTypeAndOrderIdComposite() {
        return getType1() + JOINT + getType2() + JOINT + getOrderId();
    }
}

3.4 定义接口

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import com.java.front.validation.model.OrderModelB;

@Validated
public interface BizValidateService {

    void bizMethodB(@Valid OrderModelB model, @NotNull String param);
}

3.5 接口实现

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.java.front.server.validation.model.OrderModelB;

@Component
public class BizValidateServiceImpl implements BizValidateService {

    @Override
    public void bizMethodB(@Valid OrderModelB model, @NotNull String param) {
        System.out.println("execute bizB model=" + JSON.toJSONString(model) + ",param=" + param);
    }
}

3.6 测试用例

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAuthApplication {

    @Autowired
    private BizValidateService bizValidateService;

    @Test
    public void testBizValidateB_correct() {
        OrderModelB model = new OrderModelB();
        model.setOrderId(30);
        model.setOrderName("订单名称");
        List<String> goodsList = new ArrayList<String>();
        goodsList.add("goods1");
        goodsList.add("goods2");
        goodsList.add("goods3");
        model.setGoodsList(goodsList);
        model.setAmount(new BigDecimal("10"));
        model.setType1(1);
        model.setType2(2);
        bizValidateService.bizMethodB(model, "param");
    }

    @Test
    public void testBizValidateB_error() {
        OrderModelB model = new OrderModelB();
        model.setOrderId(1);
        model.setOrderName("订单名称");
        List<String> goodsList = new ArrayList<String>();
        goodsList.add("goods1");
        goodsList.add("goods2");
        goodsList.add("goods3");
        model.setGoodsList(goodsList);
        model.setAmount(new BigDecimal("10"));
        model.setType1(1);
        model.setType2(2);
        bizValidateService.bizMethodB(model, "param");
    }
}

3.7 错误信息

javax.validation.ConstraintViolationException: bizMethodB.model.typeAndOrderIdComposite: 不满足业务条件
	at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:117)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	at com.java.front.validation.BizValidateServiceImpl$$EnhancerBySpringCGLIB$$7c26a7cb.bizMethodB(<generated>)
	at com.java.front.server.TestAuthApplication.testBizValidateB_error(TestAuthApplication.java:94)

4 文章总结

本文第一章节介绍了validation基本概念,第二章节介绍了validation基本应用,第三章节介绍了通过自定义注解动态校验字段,希望本文对大家有所帮助。


欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐