FluentValidator验证插件

作者: Ian | 2019-03-31 | 阅读
        <!-- 验证框架 -->
        <dependency>
            <groupId>com.baidu.unbiz</groupId>
            <artifactId>fluent-validator</artifactId>
            <version>1.0.9</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    @ApiOperation(value = "test", notes = "test")
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public HttpResult<EvaResultVO> test(@RequestBody Test test) {
        EmptyValidator<Object> emptyValidator = new EmptyValidator<>();
        // Result extends GenericResult<String> 源码、链式调用验证器 源码
        Result result = FluentValidator.checkAll()
                .on(test.getCode(), emptyValidator)
                .on(test.getId(), emptyValidator)
                .on(test.getDate(), emptyValidator)
                .on(test.getMiles(), new UtilValidator(Type.MONEY))
                .doValidate()
                .result(toSimple());
        CommonUtil.paramExceotion(result); // 工具类方法
        EvaResultVO evaResultVO = carEvaService.carTypeEva(carTypeAssessVO);
        return HttpResult.successResult(evaResultVO);
    }

//######################################
// 工具类方法
    /**
     * 参数校验结果处理
     *
     * @param result
     */
    public static void paramExceotion(Result result) {
        if (result == null || !result.isSuccess()) {
            throw new AssertException(ResultCodeConstants.CODE_S0006);
        }
    }


//######################################
//源码
package com.baidu.unbiz.fluentvalidator;

import com.baidu.unbiz.fluentvalidator.util.CollectionUtil;
import com.baidu.unbiz.fluentvalidator.util.Function;
import com.baidu.unbiz.fluentvalidator.util.Supplier;


/**
 * 框架自身实现的一个简单的验证结果收集器
 *
 * @author zhangxu
 * @see ResultCollector
 * @see Result
 */
public class ResultCollectors {

    /**
     * 框架提供的一个简单结果收集器
     */
    static class SimpleResultCollectorImpl implements ResultCollector<Result> {

        @Override
        public Result toResult(ValidationResult result) {
            Result ret = new Result();
            if (result.isSuccess()) {
                ret.setIsSuccess(true);
            } else {
                ret.setIsSuccess(false);
                ret.setErrors(CollectionUtil.transform(result.getErrors(), new Function<ValidationError, String>() {
                    @Override
                    public String apply(ValidationError input) {
                        return input.getErrorMsg();
                    }
                }));
            }

            return ret;
        }
    }

    /**
     * 框架提供的一个复杂结果收集器
     */
    static class ComplexResultCollectorImpl implements ResultCollector<ComplexResult> {

        @Override
        public ComplexResult toResult(ValidationResult result) {
            return newComplexResult(new Supplier<ComplexResult>() {
                @Override
                public ComplexResult get() {
                    return new ComplexResult();
                }
            }, result);
        }
    }

    /**
     * 框架提供的一个复杂结果收集器,结果对于NULL友好,即使没有任何错误{@link ComplexResult2#errors}也不会是NULL,而是一个empty list
     */
    static class ComplexResult2CollectorImpl implements ResultCollector<ComplexResult2> {

        @Override
        public ComplexResult2 toResult(ValidationResult result) {
            return newComplexResult(new Supplier<ComplexResult2>() {
                @Override
                public ComplexResult2 get() {
                    return new ComplexResult2();
                }
            }, result);
        }
    }

    /**
     * {@link #toComplex()}和{@link #toComplex2()}复用的结果生成函数
     *
     * @param supplier 供给模板
     * @param result   内部用验证结果
     * @param <T>      结果的泛型
     * @return 结果
     */
    static <T extends ComplexResult> T newComplexResult(Supplier<T> supplier, ValidationResult result) {
        T ret = supplier.get();
        if (result.isSuccess()) {
            ret.setIsSuccess(true);
        } else {
            ret.setIsSuccess(false);
            ret.setErrors(result.getErrors());
        }

        ret.setTimeElapsed(result.getTimeElapsed());
        return ret;
    }

    /**
     * 静态方法返回一个简单结果收集器
     *
     * @return 简单的结果收集器<code>ResultCollectorImpl</code>
     */
    public static ResultCollector<Result> toSimple() {
        return new SimpleResultCollectorImpl();
    }

    /**
     * 静态方法返回一个复杂结果收集器
     *
     * @return 简单的结果收集器<code>ComplexResultCollectorImpl</code>
     */
    public static ResultCollector<ComplexResult> toComplex() {
        return new ComplexResultCollectorImpl();
    }

    /**
     * 静态方法返回一个复杂结果收集器,结果对于NULL友好,即使没有任何错误{@link ComplexResult2#errors}也不会是NULL,而是一个empty list
     *
     * @return 简单的结果收集器<code>ComplexResult2CollectorImpl</code>
     */
    public static ResultCollector<ComplexResult2> toComplex2() {
        return new ComplexResult2CollectorImpl();
    }

}




package com.baidu.unbiz.fluentvalidator;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baidu.unbiz.fluentvalidator.able.ListAble;
import com.baidu.unbiz.fluentvalidator.able.ToStringable;
import com.baidu.unbiz.fluentvalidator.annotation.NotThreadSafe;
import com.baidu.unbiz.fluentvalidator.annotation.Stateful;
import com.baidu.unbiz.fluentvalidator.exception.RuntimeValidateException;
import com.baidu.unbiz.fluentvalidator.registry.Registry;
import com.baidu.unbiz.fluentvalidator.registry.impl.SimpleRegistry;
import com.baidu.unbiz.fluentvalidator.support.GroupingHolder;
import com.baidu.unbiz.fluentvalidator.util.ArrayUtil;
import com.baidu.unbiz.fluentvalidator.util.CollectionUtil;
import com.baidu.unbiz.fluentvalidator.util.Function;
import com.baidu.unbiz.fluentvalidator.util.Preconditions;
import com.baidu.unbiz.fluentvalidator.util.ReflectionUtil;
import com.baidu.unbiz.fluentvalidator.validator.element.IterableValidatorElement;
import com.baidu.unbiz.fluentvalidator.validator.element.MultiValidatorElement;
import com.baidu.unbiz.fluentvalidator.validator.element.ValidatorElement;
import com.baidu.unbiz.fluentvalidator.validator.element.ValidatorElementList;

/**
 * 链式调用验证器
 * <p/>
 * 按照<a href="https://en.wikipedia.org/wiki/Fluent_interface">Fluent Interface</a>风格实现的验证工具,以一种近似于可以语义解释的方式做对象的验证。
 * <p/>
 * 典型的调用方式如下:
 * <pre>
 * Result ret = FluentValidator.checkAll().failFast()
 *     .on(car.getLicensePlate(), new CarLicensePlateValidator())
 *     .on(car.getManufacturer(), new CarManufacturerValidator())
 *     .on(car.getSeatCount(), new CarSeatCountValidator())
 *     .doValidate().result(toSimple());
 * </pre>
 */
@NotThreadSafe
@Stateful
public class FluentValidator {

    private static final Logger LOGGER = LoggerFactory.getLogger(FluentValidator.class);

    /**
     * 验证器链,惰性求值期间就是不断的改变这个链表,及时求值期间就是遍历链表依次执行验证
     */
    private ValidatorElementList validatorElementList = new ValidatorElementList();

    /**
     * 是否一旦发生验证错误就退出,默认为true
     *
     * @see #failFast()
     * @see #failOver()
     */
    private boolean isFailFast = true;

    /**
     * 验证器上下文
     * <p/>
     * 该<tt>context</tt>可以在所有验证器间共享数据
     */
    private ValidatorContext context = new ValidatorContext();

    /**
     * 验证结果,仅内部使用,外部使用验证结果需要使用{@link #result(ResultCollector)}来做收殓处理
     */
    private ValidationResult result = new ValidationResult();

    /**
     * 默认验证回调
     */
    protected ValidateCallback defaultCb = new DefaultValidateCallback();

    /**
     * 如果启用通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,需要寻找验证器实例,这里为注册中心
     * <p/>
     * 通过{@link #configure(Registry)}来配置
     */
    private Registry registry = new SimpleRegistry();

    /**
     * 记录上一次添加的验证器数量,用于{@link #when(boolean)}做判断条件是否做当前验证
     */
    private int lastAddCount = 0;

    /**
     * 将键值对放入上下文
     *
     * @param key   键
     * @param value 值
     *
     * @return FluentValidator
     */
    public FluentValidator putAttribute2Context(String key, Object value) {
        if (context == null) {
            context = new ValidatorContext();
        }
        context.setAttribute(key, value);
        return this;
    }

    /**
     * 将闭包注入上下文
     *
     * @param key   键
     * @param value 闭包
     *
     * @return FluentValidator
     */
    public FluentValidator putClosure2Context(String key, Closure value) {
        if (context == null) {
            context = new ValidatorContext();
        }
        context.setClosure(key, value);
        return this;
    }

    /**
     * 私有构造方法,只能通过{@link #checkAll()}去创建对象
     */
    protected FluentValidator() {
    }

    /**
     * 创建<tt>FluentValidator</tt>
     *
     * @return FluentValidator
     */
    public static FluentValidator checkAll() {
        return checkAll(null);
    }

    /**
     * Groupings分组,有两个用途:
     * <ul>
     * <li>1. 当启用注解声明式验证时,用于区分是否做某次校验</li>
     * <li>1. 当Hibernate Validator时,含义和该框架内部的分组grouping相同</li>
     * </ul>
     */
    private Class<?>[] groups;

    /**
     * 排除的Groupings分组,当启用注解声明式验证时,用于区分是否做某次校验
     */
    private Class<?>[] excludeGroups;

    /**
     * 创建<tt>FluentValidator</tt>
     *
     * @param groups 分组
     *
     * @return FluentValidator
     */
    public static FluentValidator checkAll(Class... groups) {
        return new FluentValidator().setGroups(groups);
    }

    /**
     * 使用已经存在的一个验证上下文,共享context本身以及验证结果
     *
     * @param context 验证上下文
     *
     * @return FluentValidator
     */
    public FluentValidator withContext(ValidatorContext context) {
        this.context = context;
        this.result = context.result;
        return this;
    }

    /**
     * 出错即退出
     *
     * @return FluentValidator
     */
    public FluentValidator failFast() {
        this.isFailFast = true;
        return this;
    }

    /**
     * 出错不退出而继续
     *
     * @return FluentValidator
     */
    public FluentValidator failOver() {
        this.isFailFast = false;
        return this;
    }

    /**
     * 如果启用通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,需要寻找验证器实例,这里配置注册中心的步骤
     *
     * @param registry 验证器注册查找器
     *
     * @return FluentValidator
     */
    public FluentValidator configure(Registry registry) {
        Preconditions.checkNotNull(registry, "Registry should not be NULL");
        this.registry = registry;
        return this;
    }

    /**
     * 在某个对象上通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,
     * 需要保证{@link #configure(Registry)}已经先执行配置完毕<code>Registry</code>
     *
     * @param t 待验证对象
     *
     * @return FluentValidator
     */
    public <T> FluentValidator on(T t) {
        MultiValidatorElement multiValidatorElement = doOn(t);
        LOGGER.debug(multiValidatorElement + " will be performed");
        lastAddCount = multiValidatorElement.size();
        return this;
    }

    /**
     * 在某个数组对象上通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,
     * 需要保证{@link #configure(Registry)}已经先执行配置完毕<code>Registry</code>
     * <p/>
     * 注:当数组为空时,则会跳过
     *
     * @param t 待验证对象
     *
     * @return FluentValidator
     */
    public <T> FluentValidator onEach(T[] t) {
        if (ArrayUtil.isEmpty(t)) {
            lastAddCount = 0;
            return this;
        }

        return onEach(Arrays.asList(t));
    }

    /**
     * 在某个集合对象上通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,
     * 需要保证{@link #configure(Registry)}已经先执行配置完毕<code>Registry</code>
     * <p/>
     * 注:当集合为空时,则会跳过
     *
     * @param t 待验证对象
     *
     * @return FluentValidator
     */
    public <T> FluentValidator onEach(Collection<T> t) {
        if (CollectionUtil.isEmpty(t)) {
            lastAddCount = 0;
            return this;
        }

        MultiValidatorElement multiValidatorElement = null;
        for (T element : t) {
            multiValidatorElement = doOn(element);
            lastAddCount += multiValidatorElement.size();
        }
        LOGGER.debug(
                String.format("Total %d of %s will be performed", t.size(), multiValidatorElement));
        return this;
    }

    /**
     * 在某个对象上通过{@link com.baidu.unbiz.fluentvalidator.annotation.FluentValidate}注解方式的验证,
     * 需要保证{@link #configure(Registry)}已经先执行配置完毕<code>Registry</code>
     *
     * @param t 待验证对象
     *
     * @return FluentValidator
     */
    //TODO That would be much more easier if leveraging Java8 lambda feature
    protected <T> MultiValidatorElement doOn(final T t) {
        if (registry == null) {
            throw new RuntimeValidateException("When annotation-based validation enabled, one must use configure"
                    + "(Registry) method to let FluentValidator know where to search and get validator instances");
        }
        List<AnnotationValidator> anntValidatorsOfAllFields =
                AnnotationValidatorCache.getAnnotationValidator(registry, t);
        if (CollectionUtil.isEmpty(anntValidatorsOfAllFields)) {
            // no field configured with annotation
            return new MultiValidatorElement(Collections.EMPTY_LIST);
        }

        List<ValidatorElement> elementList = CollectionUtil.createArrayList();
        for (final AnnotationValidator anntValidatorOfOneField : anntValidatorsOfAllFields) {
            Object realTarget = ReflectionUtil.invokeMethod(anntValidatorOfOneField.getMethod(), t);

            if (!CollectionUtil.isEmpty(anntValidatorOfOneField.getValidators())) {
                if (!ArrayUtil.hasIntersection(anntValidatorOfOneField.getGroups(), groups)) {
                    // groups have no intersection
                    LOGGER.debug(String.format("Current groups: %s not match %s", Arrays.toString(groups),
                            anntValidatorOfOneField));
                    continue;
                }

                if (!ArrayUtil.isEmpty(excludeGroups)) {
                    if (ArrayUtil.hasIntersection(anntValidatorOfOneField.getGroups(), excludeGroups)) {
                        LOGGER.debug(String.format("Current groups: %s will be ignored because you specify %s",
                                Arrays.toString(
                                        excludeGroups), anntValidatorOfOneField));
                        continue;
                    }
                }

                for (final Validator v : anntValidatorOfOneField.getValidators()) {
                    elementList.add(new ValidatorElement(realTarget, v, new ToStringable() {
                        @Override
                        public String toString() {
                            return String.format("%s#%s@%s", t.getClass().getSimpleName(),
                                    anntValidatorOfOneField.getField().getName(), v);
                        }
                    }));
                }
            }

            // cascade handle
            if (anntValidatorOfOneField.isCascade()) {
                Field field = anntValidatorOfOneField.getField();
                if (Collection.class.isAssignableFrom(field.getType())) {
                    onEach((Collection) realTarget);
                } else if (field.getType().isArray()) {
                    onEach(ArrayUtil.toWrapperIfPrimitive(realTarget));
                } else {
                    on(realTarget);
                }
            }
        }
        MultiValidatorElement m = new MultiValidatorElement(elementList);
        validatorElementList.add(m);
        return m;
    }

    /**
     * 在待验证对象<tt>t</tt>上,使用<tt>v</tt>验证器进行验证
     *
     * @param t 待验证对象
     * @param v 验证器
     *
     * @return FluentValidator
     */
    public <T> FluentValidator on(T t, Validator<T> v) {
        Preconditions.checkNotNull(v, "Validator should not be NULL");
        composeIfPossible(v, t);
        doAdd(new ValidatorElement(t, v));
        lastAddCount = 1;
        return this;
    }

    /**
     * 在待验证对象<tt>t</tt>上,使用<tt>chain</tt>验证器链进行验证
     *
     * @param t     待验证对象
     * @param chain 验证器链
     *
     * @return FluentValidator
     */
    public <T> FluentValidator on(T t, ValidatorChain chain) {
        Preconditions.checkNotNull(chain, "ValidatorChain should not be NULL");
        final FluentValidator self = this;
        if (CollectionUtil.isEmpty(chain.getValidators())) {
            lastAddCount = 0;
        } else {
            for (Validator v : chain.getValidators()) {
                composeIfPossible(v, t);
                doAdd(new ValidatorElement(t, v));
            }
            lastAddCount = chain.getValidators().size();
        }

        return this;
    }

    /**
     * 在待验证对象数组<tt>t</tt>上,使用<tt>v</tt>验证器进行验证
     * <p/>
     * 注:当数组为空时,则会跳过
     *
     * @param t 待验证对象数组
     * @param v 验证器
     *
     * @return FluentValidator
     */
    public <T> FluentValidator onEach(T[] t, final Validator<T> v) {
        Preconditions.checkNotNull(v, "Validator should not be NULL");
        if (ArrayUtil.isEmpty(t)) {
            lastAddCount = 0;
            return this;
        }

        return onEach(Arrays.asList(t), v);
    }

    /**
     * 在待验证对象集合<tt>t</tt>上,使用<tt>v</tt>验证器进行验证
     * <p/>
     * 注:当集合为空时,则会跳过
     *
     * @param t 待验证对象集合
     * @param v 验证器
     *
     * @return FluentValidator
     */
    public <T> FluentValidator onEach(Collection<T> t, final Validator<T> v) {
        Preconditions.checkNotNull(v, "Validator should not be NULL");
        if (CollectionUtil.isEmpty(t)) {
            lastAddCount = 0;
        } else {
            List<ValidatorElement> elementList = CollectionUtil.transform(t, new Function<T, ValidatorElement>() {
                @Override
                public ValidatorElement apply(T elem) {
                    composeIfPossible(v, elem);
                    return new ValidatorElement(elem, v);
                }
            });
            lastAddCount = t.size();
            doAdd(new IterableValidatorElement(elementList));
        }

        return this;
    }

    /**
     * 将验证对象及其验证器放入{@link #validatorElementList}中
     *
     * @param listAble 验证对象及其验证器封装类
     */
    protected void doAdd(ListAble<ValidatorElement> listAble) {
        validatorElementList.add(listAble);
        LOGGER.debug(listAble + " will be performed");
    }

    /**
     * 当满足<code>expression</code>条件时,才去使用前一个{@link Validator}或者{@link ValidatorChain}来验证
     *
     * @param expression 满足条件表达式
     *
     * @return FluentValidator
     */
    public FluentValidator when(boolean expression) {
        if (!expression) {
            for (int i = 0; i < lastAddCount; i++) {
                validatorElementList.getList().removeLast();
            }
        }
        return this;
    }

    /**
     * 按照默认验证回调条件,开始使用验证
     *
     * @return FluentValidator
     */
    public FluentValidator doValidate() {
        return doValidate(defaultCb);
    }

    /**
     * 按照指定验证回调条件,开始使用验证
     *
     * @param cb 验证回调
     *
     * @return FluentValidator
     *
     * @see ValidateCallback
     */
    public FluentValidator doValidate(ValidateCallback cb) {
        Preconditions.checkNotNull(cb, "ValidateCallback should not be NULL");
        if (validatorElementList.isEmpty()) {
            LOGGER.debug("Nothing to validate");
            return this;
        }
        context.setResult(result);

        LOGGER.debug("Start to validate through " + validatorElementList);
        long start = System.currentTimeMillis();
        try {
            GroupingHolder.setGrouping(groups);
            for (ValidatorElement element : validatorElementList.getAllValidatorElements()) {
                Object target = element.getTarget();
                Validator v = element.getValidator();
                try {
                    if (v.accept(context, target)) {
                        if (!v.validate(context, target)) {
                            result.setIsSuccess(false);
                            if (isFailFast) {
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                    try {
                        v.onException(e, context, target);
                        cb.onUncaughtException(v, e, target);
                    } catch (Exception e1) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.error(v + " onException or onUncaughtException throws exception due to " + e1
                                    .getMessage(), e1);
                        }
                        throw new RuntimeValidateException(e1);
                    }
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.error(v + " failed due to " + e.getMessage(), e);
                    }
                    throw new RuntimeValidateException(e);
                }
            }

            if (result.isSuccess()) {
                cb.onSuccess(validatorElementList);
            } else {
                cb.onFail(validatorElementList, result.getErrors());
            }
        } finally {
            GroupingHolder.clean();
            int timeElapsed = (int) (System.currentTimeMillis() - start);
            LOGGER.debug("End to validate through" + validatorElementList + " costing " + timeElapsed + "ms with "
                    + "isSuccess=" + result.isSuccess());
            result.setTimeElapsed(timeElapsed);
        }
        return this;
    }

    /**
     * 转换为对外的验证结果,在<code>FluentValidator.on(..).on(..).doValidate()</code>这一连串“<a href="https://en.wikipedia
     * .org/wiki/Lazy_evaluation">惰性求值</a>”计算后的“及时求值”收殓出口。
     * <p/>
     * &lt;T&gt;是验证结果的泛型
     *
     * @param resultCollector 验证结果收集器
     *
     * @return 对外验证结果
     */
    public <T> T result(ResultCollector<T> resultCollector) {
        return resultCollector.toResult(result);
    }

    /**
     * 设置分组
     *
     * @param groups 分组
     *
     * @return FluentValidator
     */
    public FluentValidator setGroups(Class<?>[] groups) {
        this.groups = groups;
        return this;
    }

    /**
     * 设置是否快速失败
     *
     * @param isFailFast 是否快速失败
     *
     * @return FluentValidator
     */
    public FluentValidator setIsFailFast(boolean isFailFast) {
        this.isFailFast = isFailFast;
        return this;
    }

    /**
     * 设置排除的分组
     *
     * @param excludeGroups 排除分组
     *
     * @return FluentValidator
     */
    public FluentValidator setExcludeGroups(Class<?>[] excludeGroups) {
        this.excludeGroups = excludeGroups;
        return this;
    }

    /**
     * 如果验证器是一个{@link ValidatorHandler}实例,那么可以通过{@link ValidatorHandler#compose(FluentValidator, ValidatorContext, Object)}
     * 方法增加一些验证逻辑
     *
     * @param v 验证器
     * @param t 待验证对象
     */
    private <T>  void composeIfPossible(Validator<T> v, T t) {
        final FluentValidator self = this;
        if (v instanceof ValidatorHandler) {
            ((ValidatorHandler) v).compose(self, context, t);
        }
    }
}

文章中若有不足之处,还望指出。坚持是一种精神,分享是一种快乐!


版权声明:本文由 Ian 在 2019年03月31日发表。本文采用CC BY-NC-SA 4.0许可协议,非商业转载请注明出处,不得用于商业目的。
文章题目及链接:《FluentValidator验证插件》




  相关文章:


留言区:

TOP