山海FormRules
基于SpringBoot 的参数校验组件(告别HibernateValidator,享受私家定制)
ShanHaiFormRules-based SpringBoot Parameter Rules Check Component
# 组件能力
支持注解方式执行参数检验(类似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时,则判定任务执行失败。