SpringBoot aop + 反射 + MyBatis自定义生成id(雪花算法)
1 雪花算法
https://glory.blog.csdn.net/article/details/82054196
import java.util.ArrayList;
import java.util.List;
/**
* 描述: Twitter的分布式自增ID雪花算法snowflake (Java版)
*
* @author
* @create
**/
public class SnowFlake {
/**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L;
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 10; //序列号占用的位数
private final static long MACHINE_BIT = 3; //机器标识占用的位数
private final static long DATACENTER_BIT = 3;//数据中心占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; //数据中心
private long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
//sequence = 0L;
//单数毫秒为1,双数毫秒为0
sequence = currStmp % 2 == 0 ? 0L : 1L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}
private long getNewstmp() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowFlake snowFlake = new SnowFlake(1, 1);
List<Long> list = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
long id =snowFlake.nextId();
if(!list.contains(id)) {
System.out.println(id);
list.add(id);
}else {
System.out.println("出现重复了");
}
}
System.out.println(list.size());
System.out.println(System.currentTimeMillis() - start);
}
public static Long getId() {
SnowFlake snowFlake = new SnowFlake(1,1);
return snowFlake.nextId();
}
}
2. aop + 反射
- aop
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IncrementId {
}
- aop + 反射
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* @author linxq
* @describe 数据库insert id动态生成(雪花算法)切面处理类;order的值越小,优先级越高,默认值Integer.MAX
* 主键必须是Long
*/
@Aspect
@Order(-1)
@Component
public class IncrementIdAspect {
public static final Logger logger = LoggerFactory.getLogger(IncrementIdAspect.class);
private static final String mapperExt = "MAPPEREXT";
private static final String insert = "INSERT";
// 主键标识:优先级依次递减;设置的属性id,自动生成id(雪花算法)
private static final List<String> ids = Arrays.asList("id");
private final SnowFlake snowFlake = new SnowFlake(0, 0);
// 定义切入点
@Pointcut("execution(public * com.xx.server.business.dao.ex.*.insert*(..))")
public void cut() {
}
@Before("cut()")
public void before(JoinPoint point) {
setIncrementId(point);
}
// 设置自增id
private void setIncrementId(JoinPoint joinPoint) {
// 获取dao继承的代理对象(子类)
Type[] genericInterfaces = AopUtils.getTargetClass(joinPoint.getTarget()).getGenericInterfaces();
if (!isContainerIncrementId(joinPoint, genericInterfaces)) {
return;
}
// 1. 获取类名
Type type = genericInterfaces[0];
String clazzName = type.getTypeName();
if (clazzName.toUpperCase().endsWith(mapperExt)) {
// 2. 获取method
String method = joinPoint.getSignature().getName();
if (method.toUpperCase().startsWith(insert)) {
Object[] args = joinPoint.getArgs();
// 3. 获取第一个参数进行id设置
if (Objects.nonNull(args) && args.length > 0) {
Object obj = args[0];
// 4. 设置id
setId(obj);
}
} else {
logger.error("自增id不支持非insertXxx(obj):{}", method);
}
} else {
logger.error("自增id不支持非xxxMapperExt.class:{}", joinPoint.getSignature().getDeclaringType().getSimpleName());
}
}
/**
* @param joinPoint
* @param genericInterfaces
* @return
* @describe 校验class或者classExt是否存在IncrementId注解
*/
private boolean isContainerIncrementId(JoinPoint joinPoint, Type[] genericInterfaces) {
try {
// dao proxy 正常是auto下的对象
Class<?> aClass = joinPoint.getSignature().getDeclaringType();
IncrementId annotation = aClass.getDeclaredAnnotation(IncrementId.class);
if (Objects.nonNull(annotation)) {
return true;
}
// 获取代理对象,按规则获取第一个 其他情况不予处理
if (Objects.nonNull(genericInterfaces) && genericInterfaces.length == 1) {
Type type = genericInterfaces[0];
Class<?> clazz = Class.forName(type.getTypeName());
IncrementId annotation1 = clazz.getDeclaredAnnotation(IncrementId.class);
if (Objects.nonNull(annotation1)) {
return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
/**
* @param obj
* @describe 设置id
*/
private void setId(Object obj) {
Class<?> clazz = obj.getClass();
retry:
for (String idFiled : ids) {
try {
Field field = clazz.getDeclaredField(idFiled);
if (Objects.nonNull(field)) {
Method method = clazz.getDeclaredMethod("set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), Long.class);
if (Objects.nonNull(method)) {
method.invoke(obj, snowFlake.nextId());
break; // 存在设置一个值success、直接跳出
}
}
} catch (NoSuchFieldException | NoSuchMethodException e) {
continue retry;
} catch (Exception e) {
logger.error("dao 自动生成主键 error: {}", e);
}
}
}
}
- 数据库存储结果
3. 其他 AopTargetUtils 获取代理对象工具类
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Field;
public class AopTargetUtils {
/**
* 获取 目标对象
*
* @param proxy 代理对象
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy){
try {
if (!AopUtils.isAopProxy(proxy)) {
return proxy;//不是代理对象
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else { //cglib
return getCglibProxyTargetObject(proxy);
}
} catch (Exception e) {
return null;
}
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
}