看了网上很多生成自增ID的策略,最终给出的都是雪花算法,leaf算法。但是却没有满足咱们对于自定义生成规则的需求。
在业务上有一部分ID往往是有规则的,比如某个产品的订单号往往是“产品标志+时间+n位流水”,类似这样的订单规则,使用雪花算法是满足不了业务需求的,所以我们得设计一套自己的自定义ID生成器。
“产品标志+时间+n位流水”规则中,难点无非在于n位流水号的生成,因为这个流水号需要保证在多次请求中不会产生重复的订单号。
首先,咱们根据业务需求,先制定对应的规则表达式:
id:
generator:
expressions:
# 产品标志
TEST:
# 表达式,pid指的是产品标志
exp: "$(pid)$(yearMonthDayHms)$(id:6:0)"
# 字段名:初始值:最大值:最小数量:扩容数量:初始数量:增长步长
initFields: ["id"]
表达式对应的实体类:
@Data
@Configuration
@ConfigurationProperties(prefix = "id.generator")
public class IDExpressionProperties {
private Map expressions;
@Data
public static class SerialIdConfig {
// 表达式
private String exp;
// 初始化字段
private String[] initFields;
}
}
通过spring解析获得对应的IDExpressionProperties实体类,拿到咱们自定义的配置
通过规则表达式可以看出,类似“$(pid)”这样的表达式,咱们可以抽象成接口自定义生成。比如咱们定义一个VariableGenerator接口或者抽象类,遇到pid就从spring ioc容器中调用pidVariableGenerator这个Bean的生成方法获取pid的值。遇到yearMonthDayHms就调用yearMonthDayHmsVariableGenerator这个Bean的生成方法获取yearMonthDayHms指定的值。
@Configuration
public class SerialConfig {
@Autowired private IDExpressionProperties idExpressionProperties;
/**
** 咱们要创建一个ID工厂类,专门用来生成ID的类
** 使用方法:
** @Autowired
** private IDFactory idFactory;
** String id = idFactory.get("产品标志");
*/
@Bean(initMethod = "init")
public IDFactory serialIdFactory() {
return new IDFactory(idExpressionProperties.getExpressions());
}
}
变量生成器
import org.apache.commons.lang3.StringUtils;
/**
* 变量生成器
* @className VariableGenerator
* @date: 2021/2/18 下午2:53
* @description:
*/
public abstract class VariableGenerator {
public static final String COLON = ":";
/**
* apply是生成目标字符串的方法
*/
protected abstract String apply(ExpressionElement e, Expression expression);
/**
* apply的后置处理方法,默认处理字符串不足的情况下,补足对应的填充数据
*/
public String andThen(ExpressionElement e, Expression expression) {
String variableValue = apply(e, expression);
int count = e.getCount();
String fillStringValue = e.getFillStringValue();
if (StringUtils.isNotBlank(variableValue)) {
if (count > 0) {
variableValue = StringUtils.leftPad(variableValue, count, fillStringValue);
} else {
variableValue =
StringUtils.rightPad(variableValue, Math.abs(count), fillStringValue);
}
}
return variableValue;
}
}
ID生成器
初始化的时候根据表达式配置确定是哪些字段需要初始化,根据初始化字段调用指定的Bean执行初始化
通过get方法传入的参数key获取指定的规则表达式,根据指定的表达式调用对应的生成器Bean实例,调用指定的方法生成目标值,最后拼接出最终的ID
public class IDFactory {
// 变量生成器
@Autowired(required = false)
private Map variableGeneratorMap;
// 字段初始化生成器
@Autowired(required = false)
private Map initFieldGeneratorMap;
// 构造函数,参数是生成规则
public IDFactory(Map expressionMap) {
this.expressionMap = expressionMap;
}
// 实例化后执行
public void init() {
// 如果没有规则表达式,那么直接就结束
if (CollectionUti