From ef5349098dc6d3e72db1ea5c32ca0268152122ff Mon Sep 17 00:00:00 2001 From: redkale Date: Wed, 20 Dec 2023 12:04:08 +0800 Subject: [PATCH] CacheDyn --- .../java/org/redkale/asm/AsmMethodBoost.java | 62 ++++++- .../java/org/redkale/asm/AsmMethodBoosts.java | 48 ------ .../boot/watch/ServiceWatchService.java | 5 +- .../cache/spi/CacheAsmMethodBoost.java | 155 ++++++++++++++++++ .../java/org/redkale/cache/spi/CacheDyn.java | 22 +++ .../redkale/cache/spi/CacheModuleEngine.java | 12 ++ src/main/java/org/redkale/net/sncp/Sncp.java | 78 +++++++++ .../schedule/spi/ScheduleManagerService.java | 7 +- src/main/java/org/redkale/util/Utility.java | 17 ++ 9 files changed, 353 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/org/redkale/asm/AsmMethodBoosts.java create mode 100644 src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java create mode 100644 src/main/java/org/redkale/cache/spi/CacheDyn.java diff --git a/src/main/java/org/redkale/asm/AsmMethodBoost.java b/src/main/java/org/redkale/asm/AsmMethodBoost.java index ebc49d2e7..ba4e455df 100644 --- a/src/main/java/org/redkale/asm/AsmMethodBoost.java +++ b/src/main/java/org/redkale/asm/AsmMethodBoost.java @@ -28,12 +28,22 @@ public interface AsmMethodBoost { * 对方法进行动态加强处理 * * @param cw 动态字节码Writer + * @param newDynName 动态新类名 + * @param fieldPrefix 动态字段的前缀 * @param method 操作的方法 * @param newMethodName 新的方法名, 可能为null * * @return 下一个新的方法名,不做任何处理应返回参数newMethodName */ - public String doMethod(ClassWriter cw, Method method, @Nullable String newMethodName); + public String doMethod(ClassWriter cw, String newDynName, String fieldPrefix, Method method, @Nullable String newMethodName); + + /** 处理所有动态方法后调用 + * + * @param cw 动态字节码Writer + * @param newDynName 动态新类名 + * @param fieldPrefix 动态字段的前缀 + */ + public void doAfterMethods(ClassWriter cw, String newDynName, String fieldPrefix); /** * 实例对象进行操作,通常用于给动态的字段赋值 @@ -41,4 +51,54 @@ public interface AsmMethodBoost { * @param service 实例对象 */ public void doInstance(T service); + + /** + * 生产动态字节码的方法扩展器, 可以进行方法加强动作 + * + * @param 泛型 + * + * @since 2.8.0 + */ + static class AsmMethodBoosts implements AsmMethodBoost { + + private final AsmMethodBoost[] items; + + public AsmMethodBoosts(Collection list) { + this.items = list.toArray(new AsmMethodBoost[list.size()]); + } + + public AsmMethodBoosts(AsmMethodBoost... items) { + this.items = items; + } + + @Override + public String doMethod(ClassWriter cw, String newDynName, String fieldPrefix, Method method, String newMethodName) { + String newName = newMethodName; + for (AsmMethodBoost item : items) { + if (item != null) { + newName = item.doMethod(cw, newDynName, fieldPrefix, method, newName); + } + } + return newName; + } + + @Override + public void doAfterMethods(ClassWriter cw, String newDynName, String fieldPrefix) { + for (AsmMethodBoost item : items) { + if (item != null) { + item.doAfterMethods(cw, newDynName, fieldPrefix); + } + } + } + + @Override + public void doInstance(T service) { + for (AsmMethodBoost item : items) { + if (item != null) { + item.doInstance(service); + } + } + } + + } } diff --git a/src/main/java/org/redkale/asm/AsmMethodBoosts.java b/src/main/java/org/redkale/asm/AsmMethodBoosts.java deleted file mode 100644 index cb55eb6b6..000000000 --- a/src/main/java/org/redkale/asm/AsmMethodBoosts.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - */ -package org.redkale.asm; - -import java.lang.reflect.Method; -import java.util.Collection; - -/** - * 生产动态字节码的方法扩展器, 可以进行方法加强动作 - * - * @param 泛型 - * - * @since 2.8.0 - */ -class AsmMethodBoosts implements AsmMethodBoost { - - private final AsmMethodBoost[] items; - - public AsmMethodBoosts(Collection list) { - this.items = list.toArray(new AsmMethodBoost[list.size()]); - } - - public AsmMethodBoosts(AsmMethodBoost... items) { - this.items = items; - } - - @Override - public String doMethod(ClassWriter cw, Method method, String newMethodName) { - String newName = newMethodName; - for (AsmMethodBoost item : items) { - if (item != null) { - newName = item.doMethod(cw, method, newName); - } - } - return newName; - } - - @Override - public void doInstance(T service) { - for (AsmMethodBoost item : items) { - if (item != null) { - item.doInstance(service); - } - } - } - -} diff --git a/src/main/java/org/redkale/boot/watch/ServiceWatchService.java b/src/main/java/org/redkale/boot/watch/ServiceWatchService.java index 042d5c76b..6a77a53b4 100644 --- a/src/main/java/org/redkale/boot/watch/ServiceWatchService.java +++ b/src/main/java/org/redkale/boot/watch/ServiceWatchService.java @@ -7,7 +7,6 @@ package org.redkale.boot.watch; import java.lang.reflect.*; import java.util.List; -import org.redkale.annotation.Comment; import org.redkale.annotation.*; import org.redkale.boot.*; import org.redkale.convert.json.JsonConvert; @@ -69,7 +68,7 @@ public class ServiceWatchService extends AbstractWatchService { } } while ((clazz = clazz.getSuperclass()) != Object.class); if (fieldObj == null) { - return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); + return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t + ")"); } fieldObj.setAccessible(true); fieldObj.set(dest, JsonConvert.root().convertFrom(fieldObj.getGenericType(), value)); @@ -117,7 +116,7 @@ public class ServiceWatchService extends AbstractWatchService { } } while ((clazz = clazz.getSuperclass()) != Object.class); if (fieldObj == null) { - return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); + return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t + ")"); } fieldObj.setAccessible(true); return new RetResult(fieldObj.get(dest)); diff --git a/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java new file mode 100644 index 000000000..4e51c74eb --- /dev/null +++ b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java @@ -0,0 +1,155 @@ +/* + * + */ +package org.redkale.cache.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import org.redkale.asm.AnnotationVisitor; +import org.redkale.asm.AsmMethodBoost; +import org.redkale.asm.Asms; +import org.redkale.asm.ClassWriter; +import org.redkale.asm.MethodDebugVisitor; +import static org.redkale.asm.Opcodes.ACC_PRIVATE; +import static org.redkale.asm.Opcodes.ACC_PROTECTED; +import static org.redkale.asm.Opcodes.ACC_PUBLIC; +import static org.redkale.asm.Opcodes.ALOAD; +import static org.redkale.asm.Opcodes.ARETURN; +import static org.redkale.asm.Opcodes.DLOAD; +import static org.redkale.asm.Opcodes.DRETURN; +import static org.redkale.asm.Opcodes.FLOAD; +import static org.redkale.asm.Opcodes.FRETURN; +import static org.redkale.asm.Opcodes.ILOAD; +import static org.redkale.asm.Opcodes.INVOKESPECIAL; +import static org.redkale.asm.Opcodes.IRETURN; +import static org.redkale.asm.Opcodes.LLOAD; +import static org.redkale.asm.Opcodes.LRETURN; +import static org.redkale.asm.Opcodes.RETURN; +import org.redkale.asm.Type; +import org.redkale.cache.Cached; +import org.redkale.util.RedkaleException; + +/** + * + * @author zhangjx + */ +public class CacheAsmMethodBoost implements AsmMethodBoost { + + protected final Class serviceType; + + public CacheAsmMethodBoost(Class serviceType) { + this.serviceType = serviceType; + } + + @Override + public String doMethod(ClassWriter cw, String newDynName, String fieldPrefix, Method method, final String newMethodName) { + Cached cached = method.getAnnotation(Cached.class); + if (cached == null) { + return newMethodName; + } + if (Modifier.isFinal(method.getModifiers()) || Modifier.isStatic(method.getModifiers())) { + throw new RedkaleException("@" + Cached.class.getSimpleName() + " can not on final or static method, but on " + method); + } + if (!Modifier.isProtected(method.getModifiers()) && !Modifier.isPublic(method.getModifiers())) { + throw new RedkaleException("@" + Cached.class.getSimpleName() + " must on protected or public method, but on " + method); + } + int acc; + String nowMethodName; + if (newMethodName == null) { + nowMethodName = method.getName(); + acc = Modifier.isProtected(method.getModifiers()) ? ACC_PROTECTED : ACC_PUBLIC; + } else { + acc = ACC_PRIVATE; + nowMethodName = newMethodName; + } + final String rsMethodName = method.getName() + "_cache"; + { + final String cacheDynDesc = Type.getDescriptor(CacheDyn.class); + MethodDebugVisitor mv; + AnnotationVisitor av; + String[] exps = null; + Class[] expTypes = method.getExceptionTypes(); + if (expTypes.length > 0) { + exps = new String[expTypes.length]; + for (int i = 0; i < expTypes.length; i++) { + exps[i] = expTypes[i].getName().replace('.', '/'); + } + } + //需要定义一个新方法调用 this.rsMethodName + mv = new MethodDebugVisitor(cw.visitMethod(acc, nowMethodName, Type.getMethodDescriptor(method), null, exps)); + //mv.setDebug(true); + { + av = mv.visitAnnotation(cacheDynDesc, true); + av.visitEnd(); + final Annotation[] anns = method.getAnnotations(); + for (Annotation ann : anns) { + if (ann.annotationType() != Cached.class) { + Asms.visitAnnotation(mv.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); + } + } + } + { //给参数加上原有的Annotation + final Annotation[][] anns = method.getParameterAnnotations(); + for (int k = 0; k < anns.length; k++) { + for (Annotation ann : anns[k]) { + Asms.visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann); + } + } + } + mv.visitVarInsn(ALOAD, 0); + //传参数 + Class[] paramTypes = method.getParameterTypes(); + int insn = 0; + for (Class pt : paramTypes) { + insn++; + if (pt.isPrimitive()) { + if (pt == long.class) { + mv.visitVarInsn(LLOAD, insn++); + } else if (pt == float.class) { + mv.visitVarInsn(FLOAD, insn++); + } else if (pt == double.class) { + mv.visitVarInsn(DLOAD, insn++); + } else { + mv.visitVarInsn(ILOAD, insn); + } + } else { + mv.visitVarInsn(ALOAD, insn); + } + } + mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false); + if (method.getGenericReturnType() == void.class) { + mv.visitInsn(RETURN); + } else { + Class returnclz = method.getReturnType(); + if (returnclz.isPrimitive()) { + if (returnclz == long.class) { + mv.visitInsn(LRETURN); + } else if (returnclz == float.class) { + mv.visitInsn(FRETURN); + } else if (returnclz == double.class) { + mv.visitInsn(DRETURN); + } else { + mv.visitInsn(IRETURN); + } + } else { + mv.visitInsn(ARETURN); + } + } + mv.visitMaxs(20, 20); + mv.visitEnd(); + } + return rsMethodName; + } + + @Override + public void doAfterMethods(ClassWriter cw, String newDynName, String fieldPrefix) { + //do nothing + } + + @Override + public void doInstance(Object service) { + //do nothing + } + +} diff --git a/src/main/java/org/redkale/cache/spi/CacheDyn.java b/src/main/java/org/redkale/cache/spi/CacheDyn.java new file mode 100644 index 000000000..a981dc98b --- /dev/null +++ b/src/main/java/org/redkale/cache/spi/CacheDyn.java @@ -0,0 +1,22 @@ +/* + * + */ +package org.redkale.cache.spi; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.METHOD; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; + +/** + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface CacheDyn { +} diff --git a/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java b/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java index 4535602fe..1b578b16c 100644 --- a/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java +++ b/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; +import org.redkale.asm.AsmMethodBoost; import org.redkale.boot.Application; import org.redkale.boot.ModuleEngine; import org.redkale.cache.CacheManager; @@ -48,6 +49,17 @@ public class CacheModuleEngine extends ModuleEngine { return null; } + /** + * 动态扩展类的方法 + * + * @param serviceClass 类 + * + * @return 方法动态扩展器 + */ + public AsmMethodBoost createAsmMethodBoost(Class serviceClass) { + return new CacheAsmMethodBoost(serviceClass); + } + /** * 结束Application.init方法前被调用 */ diff --git a/src/main/java/org/redkale/net/sncp/Sncp.java b/src/main/java/org/redkale/net/sncp/Sncp.java index 1f1bc6910..00c94cfc0 100644 --- a/src/main/java/org/redkale/net/sncp/Sncp.java +++ b/src/main/java/org/redkale/net/sncp/Sncp.java @@ -556,6 +556,84 @@ public abstract class Sncp { mv.visitMaxs(1, 1); mv.visitEnd(); } + if (methodBoost != null) { + Class clazz = serviceImplClass; + Set methodKeys = new HashSet<>(); + do { + for (final Method method : clazz.getDeclaredMethods()) { + String mk = Utility.methodKey(method); + if (methodKeys.contains(mk)) { + //跳过已处理的继承方法 + continue; + } + methodKeys.add(mk); + String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null); + if (newMethodName != null) { + String[] exps = null; + Class[] expTypes = method.getExceptionTypes(); + if (expTypes.length > 0) { + exps = new String[expTypes.length]; + for (int i = 0; i < expTypes.length; i++) { + exps[i] = expTypes[i].getName().replace('.', '/'); + } + } + //需要定义一个新方法调用 super.method + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PRIVATE, newMethodName, Type.getMethodDescriptor(method), null, exps)); + //mv.setDebug(true); + { //给参数加上原有的Annotation + final Annotation[][] anns = method.getParameterAnnotations(); + for (int k = 0; k < anns.length; k++) { + for (Annotation ann : anns[k]) { + Asms.visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann); + } + } + } + mv.visitVarInsn(ALOAD, 0); + //传参数 + Class[] paramTypes = method.getParameterTypes(); + int insn = 0; + for (Class pt : paramTypes) { + insn++; + if (pt.isPrimitive()) { + if (pt == long.class) { + mv.visitVarInsn(LLOAD, insn++); + } else if (pt == float.class) { + mv.visitVarInsn(FLOAD, insn++); + } else if (pt == double.class) { + mv.visitVarInsn(DLOAD, insn++); + } else { + mv.visitVarInsn(ILOAD, insn); + } + } else { + mv.visitVarInsn(ALOAD, insn); + } + } + mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false); + if (method.getGenericReturnType() == void.class) { + mv.visitInsn(RETURN); + } else { + Class returnclz = method.getReturnType(); + if (returnclz.isPrimitive()) { + if (returnclz == long.class) { + mv.visitInsn(LRETURN); + } else if (returnclz == float.class) { + mv.visitInsn(FRETURN); + } else if (returnclz == double.class) { + mv.visitInsn(DRETURN); + } else { + mv.visitInsn(IRETURN); + } + } else { + mv.visitInsn(ARETURN); + } + } + mv.visitMaxs(20, 20); + mv.visitEnd(); + } + } + } while ((clazz = clazz.getSuperclass()) != Object.class); + methodBoost.doAfterMethods(cw, newDynName, FIELDPREFIX); + } cw.visitEnd(); byte[] bytes = cw.toByteArray(); Class newClazz = new ClassLoader(loader) { diff --git a/src/main/java/org/redkale/schedule/spi/ScheduleManagerService.java b/src/main/java/org/redkale/schedule/spi/ScheduleManagerService.java index c8e4f31e0..ce84c39ca 100644 --- a/src/main/java/org/redkale/schedule/spi/ScheduleManagerService.java +++ b/src/main/java/org/redkale/schedule/spi/ScheduleManagerService.java @@ -13,10 +13,12 @@ import java.time.Duration; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -131,15 +133,18 @@ public class ScheduleManagerService implements ScheduleManager, Service { Class clazz = service.getClass(); WeakReference ref = new WeakReference(service); AtomicInteger taskCount = new AtomicInteger(); + Set methodKeys = new HashSet<>(); do { for (final Method method : clazz.getDeclaredMethods()) { if (method.getAnnotation(Scheduled.class) == null) { continue; } - if (tasks.containsKey(method.getName())) { + String mk = Utility.methodKey(method); + if (methodKeys.contains(mk)) { //跳过已处理的继承方法 continue; } + methodKeys.add(mk); if (method.getParameterCount() != 0 && (method.getParameterCount() == 1 && method.getParameterTypes()[0] == ScheduleEvent.class)) { throw new RedkaleException("@" + Scheduled.class.getSimpleName() + " must be on non-parameter or " diff --git a/src/main/java/org/redkale/util/Utility.java b/src/main/java/org/redkale/util/Utility.java index 5035914a7..1cc2b685c 100644 --- a/src/main/java/org/redkale/util/Utility.java +++ b/src/main/java/org/redkale/util/Utility.java @@ -444,6 +444,23 @@ public final class Utility { return virtualExecutorConsumer == null ? defaultExecutorConsumer : virtualExecutorConsumer; } + /** + * 构建method的唯一key,用于遍历类及父类的所有方法,key过滤重载方法 + * + * @param method 方法 + * + * @return key + */ + public static String methodKey(Method method) { + StringBuilder sb = new StringBuilder(); + sb.append(method.getName()); + sb.append('-').append(method.getParameterCount()); + for (Class c : method.getParameterTypes()) { + sb.append('-').append(c.getName()); + } + return sb.toString(); + } + /** * 返回第一个不为null的对象 *