有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

SpringJava8Lambda表达式验证

我正在读关于使用谓词here进行验证的文章。我试图在SpringBoot框架中实现它,在这里我遇到了一些问题

在守则中:

public class LamdaPersonValidator implements PersonValidator {

   public void validate(Person person) {
     notNull.and(between(2, 12)).test(person.getFirstName()).throwIfInvalid("firstname");
     notNull.and(between(4, 30)).test(person.getLastName()).throwIfInvalid("secondname");
     notNull.and(between(3, 50)).and(contains("@")).test(person.getEmail()).throwIfInvalid("email");
     intBetween(0, 110).test(person.getAge()).throwIfInvalid("age");
   }

 }

没有提到验证方法中检查person对象本身是否为null的标准方法。只需要像if(persone != null) { // notNull.and..}这样设置一个空检查就可以了吗?或者有更好的方法来执行空检查

另一件事是假设,我想做一些自定义检查,比如数据库中是否存在person。在这种情况下,我需要连接到数据库以进行检查。在这种情况下,我需要自动连接接口,其中静态变量和方法是不可能的

那么,在从数据库进行验证时,使用这种方法的最佳方法是什么


共 (2) 个答案

  1. # 1 楼答案

    您也可以这样编写GenericValidator:

    为常见工作编写AbstractValidator类:

    public abstract class AbstractValidator {
    
        private Map<Predicate, String> validatorMap = new LinkedHashMap<>();
        protected List<String> messages;
    
        public AbstractValidator() {
            this.messages = new ArrayList<>();
        }
    
        protected <E> AbstractValidator add(Predicate<E> predicate, String reason) {
            validatorMap.put(predicate, reason);
            return this;
        }
    
        protected AbstractValidator apply(String fieldName, Object val) {
            AtomicBoolean flag= new AtomicBoolean(true);
            this.validatorMap.forEach((modifier, reason) -> {
                if (flag.get() && !modifier.test(val)) {
                    String message = MessageFormat.format("{0} {1}", fieldName, reason);
                    messages.add(message);
                    flag.set(false);
                }
            });
            this.validatorMap.clear();
            return this;
        }
    
        public void end(String exceptionStatus) {
            Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)
                    .orElseThrow(() -> {
                        RuntimeException ex = new RuntimeException(exceptionStatus, messages);
                        messages.clear();
                        return ex;
                    });
        }
    }
    

    编写GenericValidator类,该类将为验证实现扩展AbstractValidator

    public class GenericValidator extends AbstractValidator {
    
        private GenericValidator() {
            super();
        }
    
        public static GenericValidator of() {
            return new GenericValidator();
        }
    
        public GenericValidator nonNull() {
            add(Objects::nonNull, "Field value is null");
            return this;
        }
    
        public GenericValidator notEmpty() {
            add(StringUtils::isNotEmpty, "Field is empty");
            return this;
        }
    
        public GenericValidator min(int min) {
            add(s -> ((String) s).length() >= min, "Field min size is " + min);
            return this;
        }
    
        public GenericValidator max(int max) {
            add(s -> ((String) s).length() <= max, "Field max size is " + max);
            return this;
        }
    
        public GenericValidator notEmptyList() {
            add(CollectionUtils::isNotEmpty, "Field List is null/Empty");
            return this;
        }
    
        public GenericValidator apply(String fieldName, Object val) {
            return (GenericValidator) super.apply(fieldName, val);
        }
    }
    

    请进行相应的测试。测试用例示例:

    class GenericValidatorTest {
    
        @Test
        void genericValidationSuccessCase() {
            Abc abc = new Abc();
            abc.setName("a");
            abc.setVal(1);
            abc.setAbslist(Collections.singletonList(new ChildAbc()));
            GenericValidator of = GenericValidator.of();
            of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
            of.notEmpty().min(1).max(1).apply("name", abc.getName())
                    .nonNull().apply("value", abc.getVal())
                    .notEmptyList().apply("childAbc", abc.getAbslist())
                    .end(GENERIC_JSON_SERIALIZATION);
        }
    
        @Test
        void genericValidationWhenObjectNull() {
            GenericValidator of = GenericValidator.of();
            Assertions.assertThrows(BusinessException.class, () -> of.nonNull()
                    .apply("abc", null).end(GENERIC_JSON_SERIALIZATION));
        }
    
        @Test
        void genericValidationWithExceptionInput() {
            Abc abc = new Abc();
            abc.setName("a");
            abc.setVal(1);
            GenericValidator of = GenericValidator.of();
            of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
            GenericValidator apply = of.notEmpty().min(1).max(1).apply("name", abc.getName())
                    .nonNull().apply("value", abc.getVal())
                    .notEmptyList().apply("childAbc", abc.getAbslist());
            Assertions.assertThrows(BusinessException.class, () -> apply.end(GENERIC_JSON_SERIALIZATION));
    
        }
    }
    
    class Abc {
    
        String name;
        Integer val;
        List<ChildAbc> abslist;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getVal() {
            return val;
        }
    
        public void setVal(Integer val) {
            this.val = val;
        }
    
        public List<ChildAbc> getAbslist() {
            return abslist;
        }
    
        public void setAbslist(List<ChildAbc> abslist) {
            this.abslist = abslist;
        }
    }
    
    class ChildAbc {
    
        String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
  2. # 2 楼答案

    我们不是神圣宗教裁判所的法典法官,所以我们没有义务告诉你们,是否“可以只做一个空检查”

    当然,写一个普通的if语句是可以的,就像我们过去25年所做的那样,就像发明一个详细的框架封装null检查并以某种方式将术语“lambda”引入其中也是可以的。剩下的唯一问题是,你是否真的打算写if(person != null) { /* do the checks */ },换句话说,允许null人通过测试

    如果您想要拒绝null个人(这会更合理),那么已经有可能在不进行显式测试的情况下编写它,因为Java 7表明您不需要“使用lambdas一切都更好”框架来实现该目标。通常,与本文的示例相反,您可以使用常规代码合理地编写验证代码,使用&&运算符等简单工具,并将常见代码放入方法中:

    public void validate(Person person) {
        Objects.requireNonNull(person, "person is null");
        checkString(person.getFirstName(), "first name", 2, 12);
        checkString(person.getLastName(), "last name", 4, 30);
        checkString(person.getEmail(), "email", 3, 50);
        if(!person.getEmail().contains("@"))
            throw new IllegalArgumentException("invalid email format");
        checkBounds(person.getAge(), "age", 0, 110);
    }
    private void checkString(String nameValue, String nameType, int min, int max) {
        Objects.requireNonNull(nameValue, () -> nameType+" is null");
        checkBounds(nameValue.length(), nameType, min, max);
    }
    private void checkBounds(int value, String valueType, int min, int max) {
        if(value < min || value > max)
            throw new IllegalArgumentException(valueType+" is not within ["+min+" "+max+']');
    }
    

    这与您的代码相同,没有任何名称中带有“Lambda”的框架,仍然有可读的验证代码,并允许重用检查代码。也就是说,与反映实现方式的类名LamdaPersonValidator不同,您应该使用反映类的职责的类名。显然,负责验证对象的某些属性的验证器不应该与检查数据库中是否存在实体的验证器混淆。后者本身是一个完全不同的话题,也应该是一个独立的问题

    上面的代码只是一个如何实现与原始代码相同的示例。它不应该以这种形式出现在生产代码中,因为它是一种广泛的反模式的演示,对属性应用任意不合理的约束,很可能是程序员在编写代码时发明的

    为什么它假设一个人必须有一个名字和一个姓氏,为什么它假设一个名字必须至少有两个字符,最多12个字符,而姓氏必须在4到30个字符之间

    它实际上不是偶数字符,因为char单位和实际字符之间的关联不是1:1

    每个想实现名称验证的程序员都必须阅读Falsehoods Programmers Believe About Names (With Examples)

    同样地,Wikipedia’s List of the verified oldest people列出了100名年龄在110岁以上的人

    没有理由假设一个电子邮件地址不能超过50个字符。Atrue validation of the correct Email pattern可能是故意忽略的东西