山海FormRules

基于SpringBoot 的参数校验组件(告别HibernateValidator,享受私家定制)

ShanHaiFormRules-based SpringBoot Parameter Rules Check Component

GitHub release (latest by date) GitHub closed issues GitHub top language
GitHub Code Size GitHub Code Lines GitHub License

# 组件能力

  • 支持注解方式执行参数检验(类似ElementUI-validator风格)。

  • 提供较为常用的规则实现,同时提供了自定义规则扩展能力,

    告别Hibernate validator 分组方式无法高度定制多API共享入参的情况。

# 1.引入依赖

      <dependency>
            <groupId>com.wangshanhai.formrules</groupId>
            <artifactId>shanhai-formrules-spring-boot-starter</artifactId>
            <version>1.0.1</version>
        </dependency>

# 2.启用ShanHiFormRules 组件

使用注解@EnableShanHiFormRules即可启用ShanHiFormRules 组件

@Configuration
@EnableShanHiFormRules
public class AppConfig {
}

# 3.校验组件详细说明

注解 用途 备注
@ShanHaiForm 标记该方法需要进行参数校验
@FormRule 标记需要校验的对象 target:对象名称,即方法中的变量名
rules:规则集合
enable: 是否启用该规则(默认启用)
@Rule 规则定义 ruleType 支持:
非空校验 : RuleCollect.NOT_EMPTY
正则校验: RuleCollect.REG_EXP;
字符串长度校验: RuleCollect.STR_LENTH
枚举校验: RuleCollect.ENUM;

# 3.1 非空校验(RuleCollect.NOT_EMPTY)

   @ShanHaiForm({
            @FormRule(target = "rulesForm",rules = {
                    @Rule(ruleType = RuleCollect.NOT_EMPTY,scanFields = "*",errorCode = 1001,message = "参数不能为空!")
            })
    })
    public String restRules(@RequestBody RulesForm rulesForm) {
        return "success";
    }

scanFields :支持* 或者某个字段或者嵌套对象的字段,例如(form.zhangsan)。嵌套对象必须有明确的类型定义,否则无法支持嵌套对象的输入校验。

errorCode:异常编码

message:异常提示

对于存在异常的,会抛出如下异常:

throw new HttpFormRulesException(rule.errorCode(),field+":"+rule.message())

自己可以自行进行解析和输出。

# 3.2 正则校验(RuleCollect.REG_EXP)

   @ShanHaiForm({
            @FormRule(target = "rulesForm",rules = {
                    @Rule(ruleType = RuleCollect.REG_EXP,scanFields = "projectType", regExp = RegExpCollect.NUMBER,errorCode = 1002,message = "不符合格式要求!"
            })
    })
    public String restRules(@RequestBody RulesForm rulesForm) {
        return "success";
    }

regExp: 正则表达式,可以使用内置的一些常用正则,例如:RegExpCollect.NUMBER,也可以自己写符合自己需要的正则表达式。

# 3.3 字符串长度校验(RuleCollect.STR_LENTH)

   @ShanHaiForm({
            @FormRule(target = "rulesForm",rules = {
                    @Rule(ruleType = RuleCollect.STR_LENTH,scanFields = "projectType", min = 1,max = 30,errorCode = 1002,message = "不符合长度要求!")
            })
    })
    public String restRules(@RequestBody RulesForm rulesForm) {
        return "success";
    }

min: 字符串最小长度

max:字符串最大长度

len: 字符串固定长度

# 3.4 枚举校验(RuleCollect.ENUM)

     @ShanHaiForm({
            @FormRule(target = "rulesForm",rules = {
                    @Rule(ruleType = RuleCollect.ENUM,scanFields = "projectType", enums = {"张三","李四"},errorCode = 1002,message = "不符合格式要求!")
            })
    })
    public String restRules(@RequestBody RulesForm rulesForm) {
        return "success";
    }

enums:枚举白名单

# 3.5 扩展自己的校验方法

通过实现RuleScanService,并注册自己的规则到Spring容器中,即可实现自定义校验规则。

@Service
public class XXXRule implements RuleScanService{
    /**
     * 注册规则定义
     * @return
     */
    @Override
    public String getScanType() {
        //TODO 注册自己的校验类型(可参考RuleCollect)
        return XXXCollect.XXX; 
    }
    /**
     *  扫描目标对象
     * @param target 目标对象实例
     * @param scanFields 目标对象扫描字段 
     * @param execTarget 目标方法信息(跟踪日志使用)
     * @param rule 规则定义
     */
    @Override
    public void scanByScope(Object target, List<String> scanFields, String execTarget, Rule rule) {
       //TODO 实现自己的校验方法
       //当不符合预期时,使用throw new HttpFormRulesException()方式抛出异常
    }
}

# 3.6 @Rule 参数scanFields详解

scanFields字段范围 支持情况 备注
* 支持 支持自动推导当前对象以及当前对象的嵌套对象内的全部字段
currentObjField 支持 支持自动获取当前对象的全部字段信息
currentObjField.child 支持 支持自动解析嵌套对象的全部字段信息
currentObjField.child.child 支持 支持自动解析嵌套对象的嵌套对象的全部字段信息

注:如果参数为Map/JSONObject等没有明确字段定义的,不适用于本组件。

scanFields 如果为多个,使用,做分隔即可。如果scanFields中定义的字段在Object target 中不存在,则会被直接丢弃。

获取当前对象的某个字段的值,示例如下:

Object value= ObjectUtils.getFieldValueByName(target,field);

# 4.基于Redis的分布式锁组件

# 4.1 启用组件

@EnableShanHiReqLock
@Configuration
public class AppConfig {
}

# 4.2 基于redisson的RedLock实现的分布式锁

新增配置文件

shanhai:
  reqlock:
    strategy: redisson

配置RedissonClient对应的bean,示例如下:

@EnableShanHiReqLock
@Configuration
public class AppConfig {
    /**
     * 使用默认分布式锁策略,需要自行初始化RedissonClient对象
     * @return
     */
    @Bean
    public RedissonClient createClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379")
                .setDatabase(2);
        Logger.info(("[RedissonClient]-单实例模式"));
        return  Redisson.create(config);
    };
}

# 4.3 使用分布式锁

在需要加锁的地方,使用 @ReqLock 注解即可,需要注意的时,如果使用多个变量做锁Key,则需要使用multiLockTarget 配置对应的锁 key对象。

@RestController
@RequestMapping("/req")
public class ReqLockController {
    @GetMapping("/lock/{id}")
    @ReqLock(lockTarget ="id",lockName = "demoLock",lockExpireTime = 100,lockTimeOut = 3)
    public String lock(@PathVariable("id") Integer id) throws InterruptedException {
      Thread.sleep(8000);
      return "success:"+id;
    }

    @PostMapping("/lockMulti/{id}")
    @ReqLock(multiLockTarget ={"id","childForm.projectName"},lockName = "demoMultiLock",lockExpireTime = 100,lockTimeOut = 3)
    public String lockMulti(@PathVariable("id") Integer id,@RequestBody ChildForm childForm) throws InterruptedException {
        Thread.sleep(8000);
        return "success:"+id;
    }

}

@ReqLock 主要参数如下

   /**
     * 分布式锁分组名
     * @return
     */
    String lockName();
    /**
     * 分布式锁Value生成策略
     * @return
     */
    String lockValueStrategy() default "";

    /**
     * 分布式锁作用域(单一对象或某个对象的某个字段)
     * @return
     */
    String lockTarget() default "";
    /**
     * 分布式锁作用域-多个作用域(每个为单一对象或某个对象的某个字段)
     * @return
     */
    String [] multiLockTarget() default {};
    /**
     * 锁过期时间(单位:秒)
     * @return
     */
    long lockExpireTime() default 120;
    /**
     * 锁获取超时时间 (单位:秒)
     * @return
     */
    long lockTimeOut() default 5;

# 4.4 自定义分布式锁实现

自定义实现接口ShanhaiReqLock,即可完成自定义分布式锁实现,接口定义如下。

public interface ShanhaiReqLock {
    /**
     * 获取分布式锁
     * @param reqLock 分布式锁定义
     * @param extParams 分布式锁自定义参数(取自配置文件shanhai.reqlock.extParams)
     * @param lockReqFlag 分布式锁加锁标识
     * @return
     */
    public LockInfoDTO lock(ReqLock reqLock, Map<String, Object> extParams,String lockReqFlag);

    /**
     *  释放分布式锁
     * @param lockInfoDTO
     * @param reqLock
     * @param extParams
     */
    public void unlock(LockInfoDTO lockInfoDTO,ReqLock reqLock, Map<String,Object> extParams);
}

# 5.基于SQL的更新锁组件

# 5.1 启用组件

@EnableShanHiSqlLock
@Configuration
public class AppConfig {
}

# 5.2 使用组件

新增配置:

shanhai:
  sqllock:
    cacheType: redis

使用默认缓存组件,定义组件缓存:

    /**
     * 使用默认SQL锁缓存策略,需要自行初始化RedisTemplate对象
     * @return
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

使用SQL更新锁时,只需要添加注解:@SqlLock 即可。

@RestController
@RequestMapping("/sql")
public class SqlSockController {

    @PostMapping("/lock/{id}")
    @SqlLock(expressionValueTargets ={"id","childForm.projectName"},expression = "select id from t_sql_lock a where a.id={} and a.projectName={}")
    public String lockMulti(@PathVariable("id") Integer id, @RequestBody ChildForm childForm) throws InterruptedException {
        Thread.sleep(8000);
        return "success:"+id;
    }

}

@SqlLock 注解定义如下:

    /**
     * SQL表达式
     * @return
     */
    String expression();

    /**
     * SQL锁条件赋值字段
     * @return
     */
    String [] expressionValueTargets() default {};

    /**
     * 是否启用缓存(默认:启用)
     * @return
     */
    boolean cache() default true;

    /**
     * 缓存过期时间(默认:180s)
     * @return
     */
    long    cacheExpire() default 180;

如果SQLLock内的SQL表达式,在执行结果大于0时,则判定任务执行失败。