The background,

In the previous article, I shared my own implementation of Java business validation tool. Following the principle of “Don’t duplicate the wheel”, I searched online and found a friend who had implemented the same functional framework Fluent-Validator.

In general, after watching the whole function and implementation about think this is a good validation framework, but for my present usage scenarios, will feel there are some “heavyweight”, namely, some function for our usage scenario some redundant, because our usage scenario is simple and direct, namely the execution of business rules before the real business logic validation, If the verification fails, the system returns directly. In line with the principle of “Simple is best”, I extracted part of the code of Fluent-Validator and wrote a simple version of Fluent-Validator. There are only 6 classes in total. FluentValidator. Java, the Validator. Java, ValidatorContext. Java, ValidatorElement. Java, ValidatorElementList. Java, ValidateExcept Ion. Java. The following is the full code:

B: Show me your code

  • FluentValidator.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/ * * * chain calls the validator, reference fluent - simple implementation of the validator * * * https://github.com/neoremind/fluent-validator@authorDancing robots *@date2019/12/27 * /
public class FluentValidator {
    private static final Logger logger = LoggerFactory.getLogger(FluentValidator.class);

    /** * validator chain, lazy evaluation is constantly changing the list, in time evaluation is traversing the list to perform validation */
    private ValidatorElementList validatorElementList = new ValidatorElementList();

    /** * validator context */
    private ValidatorContext context = new ValidatorContext();

    /** * private constructor, can only create object */ by checkAll
    private FluentValidator(a) {}/** * Create FluentValidator object **@return* /
    public static FluentValidator checkAll(a) {
        return new FluentValidator();
    }

    /** * Use the validator to validate **@paramValidator *@return* /
    public <T> FluentValidator on(Validator<T> validator) {
        validatorElementList.add(new ValidatorElement(null, validator));
        return this;
    }

    /** * validates the specified object ** using the validator@paramT The object to be verified *@paramValidator *@return* /
    public <T> FluentValidator on(T t, Validator<T> validator) {
        validatorElementList.add(new ValidatorElement(t, validator));
        return this;
    }
    
    /** * validates the specified object ** using the validator@paramT The object to be verified *@paramValidator *@paramCondition, which adds the validator to the list of validators only if it is true@return* /
    public <T> FluentValidator on(T t, Validator<T> validator, boolean condition) {
        if (condition) {
            validatorElementList.add(new ValidatorElement(t, validator));
        }
        return this;
    }


    /** * Performs the validation logic ** in each validator@return* /
    public FluentValidator doValidate(a) {
        if (validatorElementList.isEmpty()) {
            logger.info("Nothing to validate");
            return null;
        }
        long start = System.currentTimeMillis();
        logger.info("Start to validate,validatorElementList={}", validatorElementList.toString());
        String validatorName;
        try {
            for (ValidatorElement element : validatorElementList.getList()) {
                Object target = element.getTarget();
                Validator validator = element.getValidator();
                validatorName = validator.getClass().getSimpleName();
                logger.info("{} is running", validatorName); validator.validate(context, target); }}catch (ValidateException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        } finally {
            logger.info("End to validate,time consuming {} ms", (System.currentTimeMillis() - start));
        }
        return this;
    }

    /** * puts key-value pairs into context **@paramThe key key *@paramThe value value *@return FluentValidator
     */
    public FluentValidator putAttribute2Context(String key, Object value) {
        if (context == null) {
            context = new ValidatorContext();
        }
        context.setAttribute(key, value);
        return this;
    }

    /** * get the validator context **@return* /
    public ValidatorContext getContext(a) {
        returncontext; }}Copy the code
  • Validator.java

/** * validator interface, the generic T represents the type of the object to be validated **@authorDancing robots *@date2019/12/27 * /
public interface Validator<T> {

    /** * Performs validation, and throws a ValidateException if validation fails **@paramContext verifies the context *@paramT The object to be verified */
    void validate(ValidatorContext context, T t);
}

Copy the code
  • ValidatorContext.java

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/** * The context in which the validator executes the call * 1. The data passing share in the validator * 2. Validate the resulting data cache for subsequent use@authorDancing robots *@date2019/12/27 * /
public class ValidatorContext {
    /** * all validators can share the attribute key-value pairs */ used
    private Map<String, Object> attributes;


    /** * Sets the property value **@paramThe key key *@paramThe value value * /
    public void setAttribute(String key, Object value) {
        if (attributes == null) {
            attributes = new HashMap<>();
        }
        attributes.put(key, value);
    }


    /** * gets the String value **@param key
     * @return* /
    public String getString(String key) {

        return (String) getAttribute(key);
    }

    /** * gets the Integer value **@param key
     * @return* /
    public Integer getInteger(String key) {
        return (Integer) getAttribute(key);
    }

    /** * get Boolean value **@param key
     * @return* /
    public Boolean getBoolean(String key) {
        return (Boolean) getAttribute(key);
    }


    /** * get the Long value **@param key
     * @return* /
    public Long getLong(String key) {
        return (Long) getAttribute(key);
    }

    /** * Gets the BigDecimal value **@param key
     * @return* /
    public BigDecimal getBigDecimal(String key) {
        return (BigDecimal) getAttribute(key);
    }

    /** * get the object **@param key
     * @param <T>
     * @return* /
    public <T> T getClazz(String key) {
        return (T) getAttribute(key);
    }

    /** * get the attribute **@paramThe key key *@returnValue * /
    public Object getAttribute(String key) {
        if(attributes ! =null && !attributes.isEmpty()) {
            return attributes.get(key);
        }
        return null; }}Copy the code
  • ValidatorElement.java

/** * Validator wrapper class **@authorDancing robots *@date2019/12/27 * /
public class ValidatorElement {
    /** * The object to be verified */
    private Object target;

    /** * validator */
    private Validator validator;

    public ValidatorElement(Object target, Validator validator) {
        this.target = target;
        this.validator = validator;
    }

    public Object getTarget(a) {
        return target;
    }

    public Validator getValidator(a) {
        returnvalidator; }}Copy the code
  • ValidatorElementList.java

import java.util.LinkedList;

/** * Calls the validator chain ** used within the FluentValidator@authorDancing robots *@date2019/12/27 * /
public class ValidatorElementList {
    /**
     * 验证器链表
     */
    private LinkedList<ValidatorElement> validatorElementLinkedList = new LinkedList<>();

    /** * Add the validator to the list **@param element
     */
    public void add(ValidatorElement element) {
        validatorElementLinkedList.add(element);
    }

    /** * get the list of validators **@return* /
    public LinkedList<ValidatorElement> getList(a) {
        return validatorElementLinkedList;
    }

    /** * Whether the validator list is empty **@return* /
    public boolean isEmpty(a) {
        return validatorElementLinkedList.isEmpty();
    }

    @Override
    public String toString(a) {
        StringBuffer sb = new StringBuffer();
        for (ValidatorElement element : validatorElementLinkedList) {
            sb.append("[");
            sb.append(element.getValidator().getClass().getSimpleName());
            sb.append("] - >");
        }
        returnsb.toString(); }}Copy the code
  • ValidateException.java
@date 2019/4/4 */ public class ValidateException extends RuntimeException {// Exception code private Integer code; publicValidateException() {
    }

    public ValidateException(String message) {
        super(message);
    }

    public ValidateException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) { this.code = code; }}Copy the code

Three, use sample

  • Validator definition
@Component
public class CustomerSignValidator implements Validator<String> {

    @Override
    public void validate(ValidatorContext context, String s) {
        // Simulate to obtain customer information and verify
        Customer customer = new Customer();
        if (1= =2) {
            throw new ValidateException("Failed to verify customer status");
        }
        // Store customer information in context for later use
        context.setAttribute("customer", customer); }}Copy the code
  • Using FluentValidator
// step1. Verify service rules
FluentValidator fluentValidator = FluentValidator.checkAll()
        .on(tradeTimeValidator)  // Determine the trading time
        .on(riskTradeStatusValidator)   // Determine the transaction risk control status
        .on(tradeValidateBo, productTradeStatusValidator)    // Determine the status of the traded goods
        .on(tradeValidateBo, tradeCountValidator)    // Check if the number of items traded is correct
        .on(dto.getCustomerNo(), customerSignValidator)  // Judge the status of client signing account
        .on(buyerTradeStatusValidator)  // Determine customer transaction status
        .on(tradeValidateBo, entrustOrderValidator)  // Check whether the order is valid
        .on(tradeValidateBo, buyerBalanceValidator) // Determine if the buying capital is sufficient
        .doValidate();
// Get the generated data from the validator context
CustomerVo customerVo = fluentValidator.getContext().getClazz("customer");
Copy the code

The above code can verify each service rule. The data generated during the verification process can be stored in the context, and the subsequent data can be directly obtained from the context.

The actual running examples are as follows:

[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:72] - Start to validate,validatorElementList=[TradeTimeValidator]->[RiskTradeStatusValidator]->[ProductTradeStatusValidator]->[TradeCou ntValidator]->[PriceValidator]->[CustomerSignValidator]->[BuyerTradeStatusValidator]->[BuyerBalanceValidator]-> [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - TradeTimeValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - RiskTradeStatusValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - ProductTradeStatusValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - TradeCountValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - PriceValidator is running [INFO] [http-nio-9006-exec-8]  [com.xxxx.validator.FluentValidator:79] - CustomerSignValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - BuyerTradeStatusValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:79] - BuyerBalanceValidator is running [INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:87] - End to validate,time consuming 24 msCopy the code

Four,

The above verification tools meet our current needs, and I think they are sufficient in general scenarios. Other extensibility scenarios include: If you need to configure service validators dynamically, you can write the class name of the validator to the configuration file, obtain each validator instance from the Spring container through the class name, and then perform the verification logic. To better manage the validators corresponding to each service and enable or disable the validators, you can create a management interface to display the validators corresponding to a service and enable or disable the validators.


Give the article a “like” if it’s helpful to you. If there are any inaccuracies, please point them out. Article first public number: dancing robot, welcome to scan code attention.