Spring方法级别数据校验:@Validated + MethodValidationPostProcessor优雅的完成数据校验动作【享学Spring】( 下)

简介: Spring方法级别数据校验:@Validated + MethodValidationPostProcessor优雅的完成数据校验动作【享学Spring】(下)

4、如何校验级联属性


在实际开发中,其实大多数情况下我们方法入参是个对象(甚至对象里面有对象),而不是单单平铺的参数,因此就介绍一个级联属性校验的例子


@Getter
@Setter
@ToString
public class Person {
    @NotNull
    private String name;
    @NotNull
    @Positive
    private Integer age;
    @Valid // 让InnerChild的属性也参与校验
    @NotNull
    private InnerChild child;
    @Getter
    @Setter
    @ToString
    public static class InnerChild {
        @NotNull
        private String name;
        @NotNull
        @Positive
        private Integer age;
    }
}
public interface HelloService {
    String cascade(@NotNull @Valid Person father, @NotNull Person mother);
}
@Slf4j
@Service
@Validated(Default.class)
public class HelloServiceImpl implements HelloService {
    @Override
    public String cascade(Person father, Person mother) {
        return "hello cascade...";
    }
}


运行测试用例:

    @Test
    public void test1() {
        helloService.cascade(null, null);
    }

输出如下;


cascade.father: 不能为null, cascade.mother: 不能为null

此处说明一点:若你father前面没加@NotNull,那打印的消息只有:cascade.mother: 不能为null


我把测试用例改造如下,你继续感受一把:

    @Test
    public void test1() {
        Person father = new Person();
        father.setName("fsx");
        Person.InnerChild innerChild = new Person.InnerChild();
        innerChild.setAge(-1);
        father.setChild(innerChild);
        helloService.cascade(father, new Person());
    }


错误消息如下(请小伙伴仔细观察和分析缘由):

cascade.father.age: 不能为null, cascade.father.child.name: 不能为null, cascade.father.child.age: 必须是正数


思考:为何mother的相关属性以及子属性为何全都没有校验呢?


5、循环依赖问题


上面说了Spring对@Validated的处理和对@Aysnc的代理逻辑是差不多的,有了之前的经验,很容易想到它也存在着如题的问题:比如HelloService的A方法想调用本类的B方法,但是很显然我是希望B方法的方法校验是能生效的,因此其中一个做法就是注入自己,使用自己的代理对象来调用:

vpublic interface HelloService {
    Object hello(@NotNull @Min(10) Integer id, @NotNull String name);
    String cascade(@NotNull @Valid Person father, @NotNull Person mother);
}
@Slf4j
@Service
@Validated(Default.class)
public class HelloServiceImpl implements HelloService {
    @Autowired
    private HelloService helloService;
    @Override
    public Object hello(@NotNull @Min(10) Integer id, @NotNull String name) {
        helloService.cascade(null, null); // 调用本类方法
        return null;
    }
    @Override
    public String cascade(Person father, Person mother) {
        return "hello cascade...";
    }
}


运行测试用例:

@Test
    public void test1() {
        helloService.hello(18, "fsx"); // 入口方法校验通过,内部调用cascade方法希望继续得到校验
    }


运行报错:


Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'helloServiceImpl': Bean with name 'helloServiceImpl' has been injected into other beans [helloServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
...


这个报错消息不可为不熟悉。关于此现象,之前做过非常非常详细的说明并且提供了多种解决方案,所以此处略过。


若关于此问的原因和解决方案不明白的,请移步此处:【小家Spring】使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案


虽然我此处不说解决方案,但我提供问题解决后运行的打印输出情况,供给小伙伴调试参考,此举很暖心有木有:


javax.validation.ConstraintViolationException: cascade.mother: 不能为null, cascade.father: 不能为null
...


总结


本文介绍了Spring提供给我们方法级别校验的能力,在企业应用中使用此种方式完成绝大部分的基本校验工作,能够让我们的代码更加简洁、可控并且可扩展,因此我是推荐使用和扩散的~


在文末有必要强调一点:关于上面级联属性的校验时使用的@Valid注解你使用@Validated可替代不了,不会有效果的。

至于有小伙伴私信我疑问的问题:为何他Controller方法中使用@Valid和@Validated均可,并且网上同意给的答案都是都可用,差不多???还是那句话:这是下篇文章的重点,请持续关注~


稍稍说一下它的弊端:因为校验失败它最终采用的是抛异常方式来中断,因此效率上有那么一丢丢的损耗。but,你的应用真的需要考虑这种极致性能问题吗?这才是你该思考的~

相关文章
|
3月前
|
前端开发 JavaScript Java
Spring Boot中的数据校验
Spring Boot中的数据校验
|
8天前
|
Java 应用服务中间件 Spring
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
|
2月前
|
Java Spring
|
2月前
|
存储 SQL Java
|
3月前
|
安全 Java Spring
Spring问题之如何配置Bean的初始化方法和销毁方法
Spring问题之如何配置Bean的初始化方法和销毁方法
|
3月前
|
Java Spring
Spring初始化加速的思路和方案问题之在BeanFactory#doGetBean方法中,栈状态的变化影响bean的初始化的问题如何解决
Spring初始化加速的思路和方案问题之在BeanFactory#doGetBean方法中,栈状态的变化影响bean的初始化的问题如何解决
|
3月前
|
Java 数据库连接 API
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
|
4月前
|
Java 应用服务中间件 Maven
ContextLoaderListener在Spring应用中的作用与配置方法
ContextLoaderListener在Spring应用中的作用与配置方法
|
4月前
|
存储 NoSQL Java
教程:Spring Boot与RocksDB本地存储的整合方法
教程:Spring Boot与RocksDB本地存储的整合方法
|
4月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves
下一篇
无影云桌面