Java business verification tool implementation (sequel)

Posted by freejellyfish on Sun, 28 Jun 2020 06:05:12 +0200

1, Background

In the previous article, I shared my own implementation of Java business verification tool Java business verification tool implementation , in line with the principle of "don't build wheels repeatedly", there are really like-minded friends who have achieved the same functional framework in online search fluent-validator.

After a general review of the overall functions and implementation, I think this is a good verification framework, but for my current use scenario, I will feel that there are some "heavyweights", that is, some functions are redundant for our use scenario, because our use scenario is relatively simple and direct, that is, the business rules are verified before the implementation of the real business logic, and the verification fails After that, return directly. In line with the principle of "simple is the best", so I took part of the code of fluent validator and wrote a simple version of fluent validator. There are only 6 classes in total, FluentValidator.java , Validator.java , ValidatorContext.java , ValidatorElement.java , ValidatorElementList.java , ValidateException.java . Here is the full code:

2, show me your code

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

/**
 * Chain call validator, refer to the simple implementation of fluent validator
 * https://github.com/neoremind/fluent-validator
 *
 * @author Dancing robot
 * @date 2019/12/27
 */
public class FluentValidator {
    private static final Logger logger = LoggerFactory.getLogger(FluentValidator.class);

    /**
     * Validator chain: the chain list is constantly changed during inert evaluation, and the chain list is traversed and verified in turn during timely evaluation
     */
    private ValidatorElementList validatorElementList = new ValidatorElementList();

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

    /**
     * Private constructor, can only create objects through checkAll
     */
    private FluentValidator() {
    }

    /**
     * Create a FluentValidator object
     *
     * @return
     */
    public static FluentValidator checkAll() {
        return new FluentValidator();
    }

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

    /**
     * Use validator to validate the specified object
     *
     * @param t         Object to be verified
     * @param validator Validator 
     * @return
     */
    public <T> FluentValidator on(T t, Validator<T> validator) {
        validatorElementList.add(new ValidatorElement(t, validator));
        return this;
    }
    
    /**
     * Use validator to validate the specified object
     *
     * @param t         Object to be verified
     * @param validator Validator 
     * @param condition If it is true, the verifier will be added to the verifier list
     * @return
     */
    public <T> FluentValidator on(T t, Validator<T> validator, boolean condition) {
        if (condition) {
            validatorElementList.add(new ValidatorElement(t, validator));
        }
        return this;
    }


    /**
     * Perform validation logic in each validator
     *
     * @return
     */
    public FluentValidator doValidate() {
        if (validatorElementList.isEmpty()) {
            logger.info("Nothing to validate");
        }
        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;
    }

    /**
     * Put key value pairs in context
     *
     * @param key   key
     * @param value value
     * @return FluentValidator
     */
    public FluentValidator putAttribute2Context(String key, Object value) {
        if (context == null) {
            context = new ValidatorContext();
        }
        context.setAttribute(key, value);
        return this;
    }

    /**
     * Get verifier context
     *
     * @return
     */
    public ValidatorContext getContext() {
        return context;
    }
}
  • Validator.java

/**
 * Validator interface, generic T represents the type of object to be validated
 *
 * @author Dancing robot
 * @date 2019/12/27
 */
public interface Validator<T> {

    /**
     * Execute validation and throw ValidateException if validation fails
     *
     * @param context Validation context
     * @param t       Object to be verified
     */
    void validate(ValidatorContext context, T t);
}
  • ValidatorContext.java

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

/**
 * The context of the verifier in the execution of the call
 * 1.Data transfer sharing in verifier
 * 2.Validate result data cache for subsequent use
 *
 * @author Dancing robot
 * @date 2019/12/27
 */
public class ValidatorContext {
    /**
     * Validators can share the property key value pairs used
     */
    private Map<String, Object> attributes;


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


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

        return (String) getAttribute(key);
    }

    /**
     * Get 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 Long value
     *
     * @param key
     * @return
     */
    public Long getLong(String key) {
        return (Long) getAttribute(key);
    }

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

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

    /**
     * get attribute
     *
     * @param key key
     * @return value
     */
    public Object getAttribute(String key) {
        if (attributes != null && !attributes.isEmpty()) {
            return attributes.get(key);
        }
        return null;
    }
}
  • ValidatorElement.java

/**
 * Verifier packaging class
 *
 * @author Dancing robot
 * @date 2019/12/27
 */
public class ValidatorElement {
    /**
     * 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() {
        return target;
    }

    public Validator getValidator() {
        return validator;
    }
}
  • ValidatorElementList.java

import java.util.LinkedList;

/**
 * Invoking the validator chain used within the FluentValidator
 *
 * @author Dancing robot
 * @date 2019/12/27
 */
public class ValidatorElementList {
    /**
     * Verifier list
     */
    private LinkedList<ValidatorElement> validatorElementLinkedList = new LinkedList<>();

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

    /**
     * Get verifier list
     *
     * @return
     */
    public LinkedList<ValidatorElement> getList() {
        return validatorElementLinkedList;
    }

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

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

    public ValidateException() {
    }

    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;
    }

}

3, Working with samples

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

    @Override
    public void validate(ValidatorContext context, String s) {
        // Simulate obtaining 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 subsequent use
        context.setAttribute("customer", customer);
    }
}
  • Use fluent validator
// Step 1. Business rule verification
FluentValidator fluentValidator = FluentValidator.checkAll()
        .on(tradeTimeValidator)  // Judge transaction time
        .on(riskTradeStatusValidator)   // Judge transaction risk control status
        .on(tradeValidateBo, productTradeStatusValidator)    // Judgment of trading commodity status
        .on(tradeValidateBo, tradeCountValidator)    // Judge whether the quantity of traded goods is correct
        .on(dto.getCustomerNo(), customerSignValidator)  // Judge the signing and opening status of customers
        .on(buyerTradeStatusValidator)  // Judge customer transaction status
        .on(tradeValidateBo, entrustOrderValidator)  // Judge whether the power of attorney is legal
        .on(tradeValidateBo, buyerBalanceValidator) // Judge whether the purchase fund is sufficient
        .doValidate();
// Get generated data from validator context
CustomerVo customerVo = fluentValidator.getContext().getClazz("customer");

The above code can realize the verification of each business rule. The data generated in the verification process can be stored in the context of context, and the data to be used in the future can be obtained directly from the context.

The actual operation example is as follows:

[INFO] [http-nio-9006-exec-8] [com.xxxx.validator.FluentValidator:72] - Start to validate,validatorElementList=[TradeTimeValidator]->[RiskTradeStatusValidator]->[ProductTradeStatusValidator]->[TradeCountValidator]->[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 ms

4, Summary

The above verification tools meet our current needs, and I think they are sufficient in general scenarios. In addition, the extension scenarios that can be thought of include: if there is a need for dynamic configuration of the business validator, you can write the validator class name to the configuration file, use it to obtain each instance of the validator through the class name in the spring container, and then execute the validation logic. If you want to better manage the validators corresponding to each business and the enabled and disabled status of the validators, you can consider to create a management interface, in which you can display each validator corresponding to a business and start or stop the validators.

If the article is helpful to you, please give it a compliment.

If there is something wrong, please point out.

The first official account: dancing robot, welcome to scan code.

Topics: Java github Attribute Spring