自定义validation注解:解决动态多字段联动校验问题

欢迎大家关注公众号「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」一起交流学习

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: 在Spring Validation中,你可以使用自定义注解来定义校验规则。在引用中的代码中,我们可以看到`@BankNo`注解的定义。这个注解标识了一个银行账号的校验规则。通过`validatedBy`属性指定了具体的校验逻辑的实现类`BankNoValidator`。同时,`groups`和`payload`属性可以用于对校验进行分类和负载。在引用中的代码中,我们可以看到在接口或方法中使用`@Validated`注解来启用校验功能。在例子中,`OrderDTO`对象上使用了`@Validated`注解,表示要对这个对象进行校验。而在引用中的代码中,我们可以看到`EqualsXzhValidator`类实现了`ConstraintValidator<EqualsXzh, String>`接口,其中`EqualsXzh`注解自定义注解,用于校验字符串必须是"xzh"。自定义校验规则的实现逻辑写在`isValid`方法中,根据具体的业务需求来编写校验规则。这样,在使用`@Validated`注解进行校验时,就会自动触发对应的校验规则。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring自定义注解(validation)](https://blog.csdn.net/ileopard/article/details/123485111)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于Spring Validation自定义校验注解](https://blog.csdn.net/Anenan/article/details/128004111)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值