Облегченная структура проверки параметров

задняя часть
Введение
Вас все еще беспокоят избыточные и громоздкие методы или базовая проверка границ и персонализированная проверка границ свойств объекта? Небольшая и красивая структура [базовая и настраиваемая среда проверки параметров], представленная ниже, может полностью решить все проблемы. Как фермер кода, который часто пишет бизнес-код, мы сталкивались с подобными вещами, независимо от того, являются ли параметры каждого бизнес-метода или службы RPC нулевыми, является ли строка пустой, является ли число больше или меньше установленного порога или находится в определенном интервале, является ли список сбора пустым, и некоторые определенные поля проверки контракта и т. д.; учитывая вышеперечисленные ситуации, нам может потребоваться написать код повторной проверки в каждом интерфейсе и каждом методе, избыточный код связан с бизнес-логикой Есть ли элегантное решение для полного разделения этой части кода проверки, чтобы можно было полностью разделить логику проверки и бизнес-логику? Ответ ниже.


Во-вторых, рамочный принцип
Прежде чем представить принцип структуры, позвольте мне объяснить, почему мы разрабатываем такую ​​структуру: в настоящее время бизнес-система в рамках системы электронной коммерции в основном использует централизованные службы RPC для вывода бизнес-возможностей, таких как: товарный бизнес, вся электроэнергия Бизнес Возможности, связанные с товарами в бизнесе, в основном управляются этой областью бизнеса.Наиболее базовыми из них являются: запрос товара [отдельно/пакетно], выпуск/редактирование товара, полка товара, управление SKU товара, управление товарными категориями, управление товарными запасами и т. д. ., при выполнении этих бизнес-сервисов требуется много работы для проверки правильности, достоверности и согласования параметров интерфейса сервиса, поэтому есть мотивация искать более абстрактное решение.

  1. Внутренний механизм работы фреймворка

2. Введение в роль фреймворка

   1)校验规则:是框架里最底层的校验逻辑执行单元,基础校验、边界校验、个性化校验都在这个模块里完成,
              校验规则有基础的校验规则(比如为空,为NULL等),也有个性化的校验规则(比如针对不同
              业务的特殊校验规则)大家只要实现一个校验规则的接口,就可以写自己的校验规则。
             【稍后有代码实例】
   2)校验入口:并不是所有的接口服务需要做校验,校验入口就是告诉框架那个服务接口或方法需要做参数数据
              校验。
   3)校验支架:连接校验入口与校验规则。告诉框架在那个入口的某个参数上要执行哪个校验规则。他们三者的
              关系就比如烹饪一道菜,食材、菜谱、厨师之间的关系; 食材就好比校验规则,菜谱就好比是
              校验入口,厨师就是校验支架他能按照菜谱,选择适合的食材烹饪出一道美味的佳肴!
   他们之间的关系如下:

      

3. Анализ принципа реализации

1) Давайте сначала посмотрим на принцип реализации правил проверки, код выглядит следующим образом:

Все правила проверки должны реализовывать интерфейс ParamCheck, который определяется следующим образом:

/**
 * 校验规则的定义
 * 主要有两类:
 * 1、基本数据类型的为空为NULL等基本校验,这些我已经写好
 * 2、个性化自定义的校验,比如批量查询阀值校验,这些往往有特定的场景和背景,只需要实现该接口
 */
public interface ParamCheck {

    /**
     * 所有需要校验的逻辑类,都需要实现这个方法
     * @param t  待校验的值
     * @param objectType 待校验值的数据类型
     * @param c 自定义校验时的规则内容
     * @return CheckResult 校验结果&描述
     */
    CheckResult check(Object t, Class<?> objectType, String c) throws ServiceCheckException;

    /**
     * 检验规则的名称,通过这个名称来动态找到注解里配置的校验规则类
     * @return
     */
    String name();
}

这个接口定义了两个方法:
check()方法就是所有校验规则的基本校验逻辑.
name()方法返回校验规则的名称,系统会初始化加载这些规则并置入本地内存里,之后所有的校验工作都会由这些
在内存中的校验规则类来完成。


我们来分别刨析实现一个基础的校验规则类和一个个性化的校验规则类,如下:

/**
 * 基础校验规则-对象是否为NUll的校验,最基础的校验,每一个参数都需要检验这一步
 */
@CheckRule
public class ObjectCheck implements ParamCheck {
    /**
     * 所有需要校验的逻辑类,都需要实现这个方法
     *
     * @param t          待校验的值
     * @param objectType 待校验值的数据类型
     * @param c          自定义校验时的规则内容
     * @return CheckResult 校验结果&描述
     */
    @Override
    public CheckResult check(Object t, Class<?> objectType, String c) {
        return CheckUtils.objectIsNullCheck(t);
    }

    /**
     * 检验规则的名称,通过这个名称来动态找到注解里配置的校验规则类
     *
     * @return
     */
    @Override
    public String name() {
        return Object.class.getName();
    }
}

/**
 * 个性化校验规则-列表的个数是否超越设定的阀值
 */
@CheckRule
public class MaxSizeCheck implements ParamCheck {

    private final static Logger logger = LoggerFactory.getLogger(MaxSizeCheck.class);

    /**
     * 所有需要校验的逻辑类,都需要实现这个方法
     *
     * @param t          待校验的值
     * @param objectType 待校验值的数据类型
     * @param c          自定义校验时的规则内容
     * @return CheckResult 校验结果&描述
     */
    @Override
    public CheckResult check(Object t, Class<?> objectType, String c) throws ServiceCheckException {

        CheckResult checkResult = new CheckResult(true);

        //如果校验列表的个数是否超越设定的阀值,没有传阀值过来,默认通过
        if(StringUtils.isEmpty(c)){
            return checkResult;
        }

        Integer maxSize;
        try {
            /**
              * 这里可以做优化,将所有个性化的校验条件加载时都初始化好
              */
            JSONObject objectCondition = JSON.parseObject(c);
            maxSize = objectCondition.getInteger("maxSize");

            if (null == maxSize) {
                return checkResult;
            }
        }catch (Exception e){
            logger.error("MaxSizeCheck Error: msg="+c,e);
            throw new ServiceCheckException("MaxSizeCheck Error: msg=" + c + e.getMessage());
        }

        return CheckUtils.sizeGreaterThanFeedCheck(t,maxSize,objectType);
    }

    /**
     * 检验规则的名称,通过这个名称来动态找到注解里配置的校验规则类
     *
     * @return
     */
    @Override
    public String name() {
        return this.getClass().getSimpleName();
    }
}

 Примечание: к каждому правилу необходимо добавить аннотацию, по которой система идентифицирует правила проверки и автоматически загружает их в память. 

Код аннотации правила проверки выглядит следующим образом:

/**
 * 校验规则注解,每一个校验规则类加一个这个注解,就可以通过
 * applicationContext.getBeansWithAnnotation() 完成初始化
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface CheckRule {
    String name() default ""; //校验规则名称(确保唯一)
}

Вышеприведенный код является основным кодом и механизмом работы правил проверки, поэтому правила проверки могут быть расширены горизонтально и бесконечно.Вам нужно только реализовать интерфейс ParamCheck и пометить его @CheckRule перед правилами, и система будет автоматически проверить вас Правила проверки загружаются в память. Итак, с правилами проверки, как их использовать? См. следующий код:

public class TagWriteServiceImpl implements TagWriteService {

    @Autowired
    private TagManager tagManager;

    /**
     * @param tagOption
     * @return
     * @ParameterCheck 这个注解是 接口的入参校验 通过注解+AOP完成
     */
    @ParameterCheck(exceptionHandler = ServiceCheckFailedHanlder.class)
    public ResultDO<Boolean> tagAdd(tagWriteOption tagOption) {
        
        //todo 自己的业务逻辑
        (在做业务逻辑之前,系统其实已经通过ParameterCheck这个注解对 tagWriteOption这个对象&对象
        指定的属性做了基础的校验工作)
        
        .......    
    }
}

使用刨析:

第一步:需要在待进行参数校验的方法前面加 @ParameterCheck这个注解。
第二步:在@ParameterCheck注解里指定一个校验不通过的错误信息返回对象,如上代码中的 ServiceCheckFailedHanlder
      类。这个类的具体实现如下:
 
public class ServiceCheckFailedHanlder implements ICheckFailedHandler {    
    /**
      * 框架本身是一个通用的框架,但总会有一些信息是需要定制化的,比如不同的业务代码中有自己不同的错误提示包装类等
      * 框架为了增加通用性和灵活性,这里框架只定义了一个接口ICheckFailedHandler,
      * 具体的有业务方自己去实现      
      */
    @Override
    public Object validateFailed(String msg, Class returnType, Object... args) {
         
        //todo,这里就可以写自己业务的错误信息封装代码了
        BaseResultDO result = new BaseResultDO<>();
        result.setSuccess(false);
        result.setResultCode(BaseResultTypeEnum.PARAM_ERROR.getCode());
        result.setResultMessage(msg);
        return result;
    }
}

上面说到,在做业务逻辑之前,系统其实已经通过ParameterCheck这个注解对 tagWriteOption这个对象&对象
指定的属性做了基础的校验工作,是如何做到的呢?

1、首先只要方法上有ParameterCheck这个注解,系统都会针对方法里边所有的参数进行基本的为NULL校验。实现如下:
   系统通过AOP自动拦截有ParameterCheck注解的方法。AOP拦截实现如下:

@Component
@Aspect
@Order(1)
public class ParamCheckAop  extends AbsAopServiceParamterCheck {    

     private static Logger logger = LoggerFactory.getLogger("aopServiceParameterCheck");

    /**
     * 只要有ParameterCheck 这个注解的方法,都会被拦截
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("@annotation(parameterCheck)")
    public Object around(ProceedingJoinPoint pjp, ParameterCheck parameterCheck) throws Throwable {

        long startTime = System.currentTimeMillis();

        //执行校验方法
        CheckResult checkSuccess = super.check(pjp, true);
        long annExceTime = System.currentTimeMillis() - startTime;
        if (logger.isDebugEnabled()) {
            logger.debug(pjp.getTarget().getClass().getSimpleName() + "|checkTime=" + annExceTime);
        }


        if (!checkSuccess.isSuccess()) {
            Method method = getMethod(pjp);
            ICheckFailedHandler handler = CheckFailedHandlerWrapper.getInstance()
                    .getCheckFailedHander(parameterCheck.exceptionHandler());

            return handler.validateFailed(checkSuccess.getMsg(),
                    method.getReturnType(),
                    pjp.getArgs());
        }


        return pjp.proceed();
    }

    private Method getMethod(JoinPoint pjp) {
        MethodSignature method = (MethodSignature) pjp.getSignature();
        return method.getMethod();
    }
}
 

AbsAopServiceParamterCheck是框架提供的一个抽象类,实现如下:

public abstract class AbsAopServiceParamterCheck {    
    private static Logger logger = LoggerFactory.getLogger(AbsAopServiceParamterCheck.class);

    @Resource
    private ServiceParameterCheck serviceParameterCheck;

    protected CheckResult check(ProceedingJoinPoint pjp, boolean isWriteLog) throws Throwable {

        Signature sig = pjp.getSignature();
        MethodSignature msig;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());

        //当前方法是否ParameterCheck这个注解
        if(currentMethod.isAnnotationPresent(ParameterCheck.class)){

            //方法参数
            Object[] args = pjp.getArgs();
            Object[] params = new Object[args.length+2];
            params[0] = pjp.getTarget();         //类名全路径
            params[1] = currentMethod.getName(); //方法名
            for(int i = 0;i<args.length;i++){
                params[i+2] = args[i];
            }

            //执行校验方法-参数的基本校验 + 自定义的校验
            CheckResult checkBaseParamResult = serviceParameterCheck.checkMethod(params);
            if(!checkBaseParamResult.isSuccess()){
                logger.warn(pjp.getTarget().getClass().getSimpleName()+"."+currentMethod.getName()+"|checkSuccess=false"+"|param="+ JSON.toJSONString(args));
                return checkBaseParamResult;
            }

            //执行校验方法-参数如果是自定义对象还需要校验一下 对象里的属性是否有校验规则

            CheckResult checkObjectParamResult = serviceParameterCheck.batchCheckObjecs(args);
            if(!checkObjectParamResult.isSuccess()){
                logger.warn(pjp.getTarget().getClass().getSimpleName()+"."+currentMethod.getName()+"|checkSuccess=false"+"|param="+JSON.toJSONString(args));
                return checkObjectParamResult;
            }

            if(isWriteLog){
                logger.warn("look i am here");
            }
        }
        return new CheckResult(true);
    }
}

基础的校验就是这一行代码:

CheckResult checkBaseParamResult = serviceParameterCheck.checkMethod(params);

代码如下:

@Service
public class ServiceParameterCheck implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    /**
     * 注册的初始化对象列表
     */
    private Map<String,IAnnotationManager> annotationManagerMap = new HashMap<>();

    /**
     * 在初始化类的时候执行,将每一个注册的对象的属性缓存起来
     */
    @PostConstruct
    protected void init() throws ServiceCheckException {

        Map<String,Object> objectMap = applicationContext.getBeansWithAnnotation(ServiceCheckPoint.class);

        /**  初始化-入参【对象级&方法级】的注解属性 **/
        for(Object o : objectMap.values()){
            if(o instanceof IAnnotationManager){
                annotationManagerMap.put(((IAnnotationManager)o).getAnnotationCheckType().name(),((IAnnotationManager)o));
                ((IAnnotationManager)o).init();
            }
        }
    }

    /**
     * 根据方法上的入参做校验
     * @param args
     * @return
     */
    public CheckResult checkMethod(Object ...args){
        return annotationManagerMap.get(AnnotationCheckType.METHOD.name()).check(args);
    }

    /**
     * 根据入参对象上的注解
     * @param o
     * @return
     */
    public CheckResult checkObject(Object o){
        return annotationManagerMap.get(AnnotationCheckType.OBJECT.name()).check(o);
    }

    /**
     * 根据入参对象上的注解批量校验
     * @param objects
     * @return
     */
    public CheckResult batchCheckObjecs(Object[] objects){

        IAnnotationManager iAnnotationManager = annotationManagerMap.get(AnnotationCheckType.OBJECT.name());

        if(ArrayUtils.isEmpty(objects)){
            return new CheckResult(true);
        }

        for(Object o : objects){
            Class<?> objectType = o.getClass();
            if(objectType.getSimpleName().endsWith("List")){
                o = ((List)o).get(0);
            }else if(objectType.getSimpleName().endsWith("Map")){
                o = ((Map)o).values().toArray()[0];
            }else if(objectType.getSimpleName().endsWith("Set")){
                o = ((Set)o).toArray()[0];
            }else if(objectType.isArray()){
                o = Arrays.asList(o).get(0);
            }


            CheckResult checkRult = iAnnotationManager.check(o);
            if(!checkRult.isSuccess()){
                return checkRult;
            }
        }

        return new CheckResult(true);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}  

具体校验在这里:

/**
 * 针对服务接口的方法参数做校验
 */
@ServiceCheckPoint
public class ServiceMethodAnnotationManager  implements IAnnotationManager,ApplicationContextAware {
    private final static Logger logger = LoggerFactory.getLogger(ServiceMethodAnnotationManager.class);

    /**
     * 每个服务对应的方法集合
     */
    private Map<String,Method> methodMap = new HashMap<>();

    /**
     * 每个服务对应的方法参数列表
     */
    private Map<String,List<Class<?>>> methodParamMap = new HashMap<>();

    /**
     * 前面两位用作其他用途
     */
    private final static Integer reserveLen = 2;

    @Resource
    ParamCheckManager paramCheckManager;

    private ApplicationContext applicationContext;

    @Override
    @PostConstruct
    public void init() throws ServiceCheckException {

        Map<String,Object> objectMap = applicationContext.getBeansWithAnnotation(ServiceMethodCheck.class);

        try {
            for(Object o: objectMap.values()){
                Class<?> clazz = o.getClass().getSuperclass();

                //获取方法列表,如果没有注册,直接跳出返回
                Method[] methods = clazz.getDeclaredMethods();
                if(ArrayUtils.isEmpty(methods)){
                    break;
                }

                for(Method method : methods){
                    //方法上是否有ParameterCheck这个注解
                    if(method.isAnnotationPresent(ParameterCheck.class)){
                        String key = clazz.getName() + "." + method.getName();

                        Class<?>[] parameterTypes = method.getParameterTypes();
                        if(!ArrayUtils.isEmpty(parameterTypes)) {
                            methodParamMap.put(key, Arrays.asList(parameterTypes));
                        }
                        methodMap.put(key,method);
                    }
                }
            }

            logger.warn(" ServiceMethodAnnotationManager init success ,methodMap:" + JSON.toJSONString(methodMap));

        }catch (Exception e){
            logger.error("ServiceMethodAnnotationManager error!",e);
            throw new ServiceCheckException("ServiceMethodAnnotationManager Init Error! " + e.getMessage());
        }
    }

    /**
     * 具体执行校验的入口之一
     * @param args
     * @return  CheckResult 校验结果 & 错误描述
     */
    @Override
    public CheckResult check(Object... args) {
        CheckResult checkResult = new CheckResult(true);

        /**参数列表为空,直接返回true,不做校验**/
        if(ArrayUtils.isEmpty(args)){
            return checkResult;
        }

        /**参数长度必须大于两个,第一个是接口服务类对象,第二个是调用的方法签名,剩余的是入参**/
        if(args.length < reserveLen){
            return checkResult;
        }

        Object[] objects = args;

        //第二个是调用的方法签名
        String methodName = args[1].toString();
        //类名+方法名作为key
        String key = args[0].getClass().getName()+"."+methodName;

        /** 这个类+方法下的参数列表,methodParamMap在初始化的时候已经设置好了 **/
        List<Class<?>> paramTypeNameList = methodParamMap.get(key);

        /** 说明不需要检验 **/
        if(CollectionUtils.isEmpty(paramTypeNameList)){
            return checkResult;
        }

        //获取对应key的方法对象
        Method method = methodMap.get(key);

        //获取对应方法上的注解
        ParameterCheck annotation = method.getAnnotation(ParameterCheck.class);
        if(null == annotation){
            return checkResult;
        }

        //获取方法参数里对应的注解列表
        Map<Integer,Annotation> annotationAndParamIndexMap = getAnnotationAndParamIndex(method);

        /**
         * 循环校验传入的参数,为什么要从2开始,
         * 因为第一个参数是服务对象。
         * 第二个参数是方法签名。
         * 从第三个参数开始,才是方法的参数列表
         */
        try {
            for (int i = reserveLen; i < objects.length; i++) {
                int paramIndex = i-reserveLen;

                //字段上有uncheck这个注解,说明不需要做检验,忽略
                if (isCheck(annotationAndParamIndexMap, paramIndex)) {
                    //如果参数上没有自定义注解,那么就以方法注解上的自定义注解为检验的规则
                    if(isHaveSelfCheck(annotationAndParamIndexMap, paramIndex)){
                        //参数上的自定义检验规则注解
                        SelfCheck paramAnnotation = (SelfCheck)annotationAndParamIndexMap.get(paramIndex);
                        checkResult = paramCheckManager.check(objects[i], paramTypeNameList.get(paramIndex),
                                Arrays.asList(paramAnnotation.check()),
                                paramAnnotation.condition(),
                                paramAnnotation.msg());
                    }else{
                        checkResult = paramCheckManager.check(objects[i], paramTypeNameList.get(paramIndex),
                                Arrays.asList(annotation.selfCheck()),
                                annotation.condition(),
                                annotation.msg());
                    }

                    if (!checkResult.isSuccess()) {
                        return checkResult;
                    }
                }
            }
        }catch (Exception e){
            /**如果检验里边发生了异常,默认通过校验。可以往下走*/
            logger.error("ServiceMethodAnnotationManager error ,msg=",e);
        }

        return new CheckResult(true);
    }

    /**
     * 每个实现这个接口,都需要返回一个校验的级别:方发入参级别
     * @return
     */
    @Override
    public AnnotationCheckType getAnnotationCheckType() {
        return AnnotationCheckType.METHOD;
    }

    /**
     * 获取不需要检查的参数的索引集合
     * @param method
     * @return
     */
    private Map<Integer,Annotation> getAnnotationAndParamIndex(Method method){
        Map<Integer,Annotation> annotationAndParamIndexMap = new HashMap<>();

        //获取方法参数里是否有 uncheck这个注解
        Annotation[][] annotations = method.getParameterAnnotations();
        if(!ArrayUtils.isEmpty(annotations)) {
            for (int i = 0; i < annotations.length; i++) {
                for (int j = 0; j < annotations[i].length; j++) {
                    //把参数及对应参数上的注解记录解析出来 循环下标是从0开始,i=0 实际上是指第一个参数。
                    if(null != annotations[i][j]) {
                        annotationAndParamIndexMap.put(i,annotations[i][j]);
                    }
                }
            }
        }

        return annotationAndParamIndexMap;
    }

    /**
     * 字段上有uncheck这个注解,不需要做检验,忽略
     * @param annotationAndParamIndexMap
     * @param paramIndex
     * @return
     */
    private boolean isCheck(Map<Integer,Annotation> annotationAndParamIndexMap,Integer paramIndex) {
        if(CollectionUtils.isEmpty(annotationAndParamIndexMap)){
            return true;
        }

        //如果对应的参数里包含 Uncheck这个注解,就返回false,不校验这个参数
        if(annotationAndParamIndexMap.get(paramIndex) instanceof Uncheck){
            return false;
        }

        return true;
    }

    /**
     * 字段上是否有 自定义的注解 selfCheck
     * @param annotationAndParamIndexMap
     * @param paramIndex
     * @return
     */
    private boolean isHaveSelfCheck(Map<Integer,Annotation> annotationAndParamIndexMap,Integer paramIndex){
        if(CollectionUtils.isEmpty(annotationAndParamIndexMap)){
            return false;
        }

        if(annotationAndParamIndexMap.get(paramIndex) instanceof SelfCheck){
            return true;
        }

        return false;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}          

这一行代码就是具体的校验
checkResult = paramCheckManager.check(objects[i], paramTypeNameList.get(paramIndex),
                                Arrays.asList(paramAnnotation.check()),
                                paramAnnotation.condition(),
                                paramAnnotation.msg());

我们看下paramCheckManager的实现如下:

@Service
public class ParamCheckManager {
    @Resource
    private ParamCheckCollection paramCheckCollection;

    /**
     * 基础校验&自定义校验
     * 基础校验是必须要做的,自定义校验根据注解上的配置来决定是否要做
     * @param v
     * @param objectType
     * @param selfChecks
     * @param condition
     * @param failMsg
     * @return
     */
    public CheckResult check(Object v,
                             Class<?> objectType,
                             List<String> selfChecks,
                             String condition,
                             String failMsg) throws ServiceCheckException {

        //基础的校验,对象为NULL,为EMPTY
        CheckResult baseCheck = paramCheckCollection.getParamCheckInstance(objectType.getName()).check(v, objectType, condition);

        //基础校验不通过,直接返回false
        if (!baseCheck.isSuccess()) {
            return baseCheck;
        }

        //加载自定义的数据校验
        if (!CollectionUtils.isEmpty(selfChecks)) {
            for (String selfCheck : selfChecks) {
                if (!StringUtils.isEmpty(selfCheck)) {
                    CheckResult checkRult = paramCheckCollection.getParamCheckInstance(selfCheck).check(v, objectType, condition);

                    //自定义校验不通过,直接返回false
                    if (!checkRult.isSuccess()) {
                        // 使用用户自定义的校验失败信息
                        if (StringUtils.isNotBlank(failMsg)) {
                            checkRult.setMsg(failMsg);
                        }
                        return checkRult;
                    }
                }
            }
        }

        return new CheckResult(true);
    }
}

其中 ParamCheckCollection这里封装了所有的校验规则(包括框架里的和将来自己要写的都是在这里加载完成的)

/**
 * 所有注册的检验类(对象类型的、集合类型的、自定义类型等)
 */
@Service
public class ParamCheckCollection  implements ApplicationContextAware {
    private Map<String,ParamCheck> paramCheckMap;
    private ApplicationContext applicationContext;

    /**
     * 初始化完成所有校验的规则类,并按照名称植入map中
     */
    @PostConstruct
    protected void init(){
        Map<String,Object> tempParamCheckMap = applicationContext.getBeansWithAnnotation(CheckRule.class);

        if(!CollectionUtils.isEmpty(tempParamCheckMap)){
            paramCheckMap = new HashMap<>(tempParamCheckMap.size());
            for(Object o : tempParamCheckMap.values()){
                if(o instanceof ParamCheck){
                    ParamCheck paramCheck = (ParamCheck)o;
                    paramCheckMap.put(paramCheck.name(),paramCheck);
                }
            }
        }
    }

    /**
     * 返回对应规则名称的校验类,如果没有找到对应的规则类,那么返回对象检验规则类
     * @param checkName
     * @return
     */
    public ParamCheck getParamCheckInstance(String checkName){
        if(StringUtils.isEmpty(checkName)){
            return paramCheckMap.get(Object.class.getName());
        }
        ParamCheck iParamCheck = paramCheckMap.get(checkName);
        return (null != iParamCheck)? iParamCheck : paramCheckMap.get(Object.class.getName());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

2、对象属性上的校验是如何做到的呢?
  待校验的对象代码如下:
  @MinNum 就是框架里的一个个性化校验规则:校验数字的大小是否大于某个数字,数字由业务自己定义。
  @ParameterCheck 是一个基础校验规则:校验对象是否为NULL。
  属性前没有注解的,默认不需要进行校验,框架就不会去校验这些属性。
  校验逻辑,如上面所讲,框架会加载类里的属性并,检查类的属性里是否有注解。

public class TagWriteOption implements Serializable {
    private static final long serialVersionUID = -1639997547043197452L;

    /**
     * 商品id,必填
     */
    @MinNum(value = 1, msg = "itemId需要大于0")
    private Long itemId;

    /**
     * 商品所属市场
     */
    @ParameterCheck(msg = "market不能为空")
    private Integer market;


    /**
     * 调用的业务系统的名称
     */
    private String appName;
}

3、方法上有ParameterCheck这个注解,系统都会针对方法里边所有的参数进行基本的为NULL校验,
   那如果有些方法的参数我不想做校验,如何实现?
   
   框架考虑到了这个情况,您只需要在该参数前面加@Uncheck注解,框架检测到参数前有这个注解,
   就会忽略这个参数的校验,上面的代码中有实现。

/**
 * 不需要检查
*/
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Uncheck {}

На данный момент вы уже знакомы с основными принципами реализации и кодами этой структуры, а также знаете, как определить собственные правила проверки и применить их к собственным бизнес-сценариям. Полностью прозрачная и расширяемая структура фактически определяет общий стандарт выполнения правил и определения правил, а также интегрирует основные правила проверки. Вы можете полностью выполнить вторичную разработку и определение правил на основе исходного кода платформы, чтобы добиться более персонализированной проверки бизнес-сценария.

Аннотации проверки, которые в настоящее время поддерживаются платформой, следующие:

@MinNum Проверка минимального числового значения

@MaxNum Проверка числового максимального значения

@MinSize установить проверку минимального числа

@MaxSize в сочетании с максимальным количеством проверок

@StrSize Проверка длины строки

@NotNull ненулевая проверка

Пользовательская проверка @SelfDef

@Num Проверка числового интервала

Проверка интервала сбора @CollectionSize


4. Обзор функций платформы

1) Изоляция кода, которая полностью изолирует и отделяет логику кода проверки от кода бизнес-логики.

Код для проверки данных полностью независимо инкапсулирован в одно за другим правила проверки.Вы можете поддерживать свою собственную библиотеку правил проверки.Вам нужно только настроить аннотацию + имя правила при ее использовании.


2) Чрезвычайно высокая масштабируемость, а правила проверки могут поддерживать неограниченное горизонтальное расширение в соответствии с реальной ситуацией бизнес-сценария.


3) Отличная производительность, все классы загружаются в память путем предзагрузки модулей, не нужно создавать объекты при использовании.


4) После того, как вторжение кода 0 завершено, вам нужно всего лишь настроить несколько аннотаций, чтобы реализовать базовую и персонализированную проверку данных входных параметров без какого-либо загрязнения бизнес-кода.

5) Интеллектуальная проверка, платформа может осуществлять интеллектуальную проверку данных в соответствии с типом данных параметра и выполнять соответствующую базовую проверку для типа параметра, например, объект является нулевым, строка пуста, размер списка и т. д., пользовательские объекты, выполните необходимую проверку в соответствии с аннотациями к свойствам объекта.

6) Результаты проверки могут быть настроены по мере необходимости.Различные бизнес-поля или коды имеют различную информацию о подсказке для возврата ошибки проверки входных данных.Структура предоставляет гибкую информацию о результатах проверки, и требуется только реализовать интерфейс возврата исключения, а именно Может.

7) Настройка правил проверки гибкая.По умолчанию все параметры будут проверяться базовой проверкой, а указанные параметры также могут быть проверены.Конечно, также можно указать, что проверка не требуется.

8) Код широко используется повторно. Для базовой проверки его нужно только инкапсулировать в базовое правило проверки. Для последующего использования требуется только настроить аннотацию, которую можно использовать повторно, что косвенно повышает эффективность разработки и затраты на обслуживание кода.


В этой статье в основном представлены основные принципы фреймворка и способы его использования.Из-за нехватки места и личных возможностей, если у вас возникнут какие-либо вопросы или проблемы в процессе чтения, вы можете задать их напрямую.Вы можете выдвигать любые предложения и мнения по улучшению этого фреймворка, чтобы он стал лучше, ценнее и значимее!

Фреймворк был доработан мной и еще одним коллегой в компании.Если у вас есть вопросы, пишите нам wangchun_166@126.com, yihuihuiyi@gmail.com, мы всегда ждем ваших предложений и замечаний!