From afec947e078f1cd70e34052beeb8c39039f95f4f Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 08:49:31 +0800 Subject: [PATCH 01/32] build: add jandex dependency --- microsphere-redis-parent/pom.xml | 7 +++++++ microsphere-redis-spring/pom.xml | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/microsphere-redis-parent/pom.xml b/microsphere-redis-parent/pom.xml index 02c34be..e8a862c 100644 --- a/microsphere-redis-parent/pom.xml +++ b/microsphere-redis-parent/pom.xml @@ -28,6 +28,7 @@ 3.1.6 2021.0.5.0 2021.0.7 + 3.1.6 @@ -107,6 +108,12 @@ import + + + io.smallrye + jandex + ${jandex.version} + diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index 46e012c..06b5dfa 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -66,6 +66,11 @@ snakeyaml + + io.smallrye + jandex + true + junit From 8fec691f0985f713b3ec43e2fc3772d27219404c Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 10:22:50 +0800 Subject: [PATCH 02/32] feat: get method info from RedisCommand class --- .../metadata/RedisCommandsMethodHandles.java | 84 +++++++++++++++++++ .../RedisCommandsMethodHandlesTest.java | 43 ++++++++++ 2 files changed, 127 insertions(+) create mode 100644 microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java create mode 100644 microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java new file mode 100644 index 0000000..436e66c --- /dev/null +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -0,0 +1,84 @@ +package io.microsphere.redis.spring.metadata; + +import org.jboss.jandex.Index; +import org.jboss.jandex.MethodInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.connection.RedisCommands; +import org.springframework.data.redis.connection.RedisConnectionCommands; +import org.springframework.data.redis.connection.RedisGeoCommands; +import org.springframework.data.redis.connection.RedisHashCommands; +import org.springframework.data.redis.connection.RedisHyperLogLogCommands; +import org.springframework.data.redis.connection.RedisKeyCommands; +import org.springframework.data.redis.connection.RedisListCommands; +import org.springframework.data.redis.connection.RedisPubSubCommands; +import org.springframework.data.redis.connection.RedisScriptingCommands; +import org.springframework.data.redis.connection.RedisServerCommands; +import org.springframework.data.redis.connection.RedisSetCommands; +import org.springframework.data.redis.connection.RedisStreamCommands; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.connection.RedisTxCommands; +import org.springframework.data.redis.connection.RedisZSetCommands; + +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class RedisCommandsMethodHandles { + private static final Logger logger = LoggerFactory.getLogger(RedisCommandsMethodHandles.class); + private static List> TARGET_CLASSES; + + /** + * get all public methods from {@link RedisCommands}
+ * exclude {@link RedisCommands#execute}
+ * exclude private lambda method from {@link RedisStreamCommands} + *
  • lambda$xDel$1
  • + *
  • lambda$xAck$0
  • + * + * @return + */ + static List getAllRedisCommandMethods() { + try { + Index index = Index.of(TARGET_CLASSES); + + return index.getClassByName(RedisCommands.class) + .interfaceNames() + .stream() + .map(index::getClassByName) + .flatMap(classInfo -> classInfo.methods().stream()) + .filter(methodInfo -> Modifier.isPublic(methodInfo.flags())) + .collect(Collectors.toList()); + } catch (IOException e) { + + logger.error("Can't get RedisCommands Methods", e); + + throw new RuntimeException("Can't get RedisCommands Methods"); + } + } + static Map initRedisCommandMethodHandle() { + return null; + } + + static { + TARGET_CLASSES = new ArrayList<>(); + TARGET_CLASSES.add(RedisCommands.class); + TARGET_CLASSES.add(RedisKeyCommands.class); + TARGET_CLASSES.add(RedisStringCommands.class); + TARGET_CLASSES.add(RedisListCommands.class); + TARGET_CLASSES.add(RedisSetCommands.class); + TARGET_CLASSES.add(RedisZSetCommands.class); + TARGET_CLASSES.add(RedisHashCommands.class); + TARGET_CLASSES.add(RedisTxCommands.class); + TARGET_CLASSES.add(RedisPubSubCommands.class); + TARGET_CLASSES.add(RedisConnectionCommands.class); + TARGET_CLASSES.add(RedisServerCommands.class); + TARGET_CLASSES.add(RedisStreamCommands.class); + TARGET_CLASSES.add(RedisScriptingCommands.class); + TARGET_CLASSES.add(RedisGeoCommands.class); + TARGET_CLASSES.add(RedisHyperLogLogCommands.class); + } +} diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java new file mode 100644 index 0000000..856638c --- /dev/null +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -0,0 +1,43 @@ +package io.microsphere.redis.spring.metadata; + +import org.jboss.jandex.MethodInfo; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.connection.RedisCommands; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; + +class RedisCommandsMethodHandlesTest { + + private Class redisCommandClass = RedisCommands.class; + + static int methodCount = RedisCommands.class.getMethods().length - 1; + + @Test + void shouldGetAllMethodInfoFromRedisCommand() { + List list = getAllRedisCommandMethods(); + + assertThat(list) + .isNotNull() + .hasSize(methodCount); + } + + @Disabled + @Test + void shouldGetMethodHandleFroMethodInfo() { + Map map = initRedisCommandMethodHandle(); + assertThat(map) + .isNotNull() + .hasSize(methodCount); + assertThatNoException().isThrownBy(RedisCommandsMethodHandles::initRedisCommandMethodHandle); + } +} From 3222193f4caf3abbac9a04dc0b3aa3777c2e82c3 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 10:48:55 +0800 Subject: [PATCH 03/32] build(redis-spring-test): add mockito in test --- microsphere-redis-spring/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index 06b5dfa..afef156 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -103,5 +103,11 @@ test
    + + org.mockito + mockito-inline + test + + \ No newline at end of file From ca4a1f85ee05dc20e0f2c21cadcb879d8f336ad6 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 10:50:38 +0800 Subject: [PATCH 04/32] feat: MethodInfoList to MethodHandleMap --- .../metadata/RedisCommandsMethodHandles.java | 36 ++++++++++++++++++- .../RedisCommandsMethodHandlesTest.java | 27 +++++++------- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 436e66c..d4fbe70 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -59,7 +59,41 @@ static List getAllRedisCommandMethods() { throw new RuntimeException("Can't get RedisCommands Methods"); } } - static Map initRedisCommandMethodHandle() { + + static Map initRedisCommandMethodHandle() { + List methods = getAllRedisCommandMethods(); + + return methods.stream() + .map(methodInfo -> { + String methodSignature = methodInfo.toString(); + MethodHandle methodHandle = generateMethodHandle(); + return new MethodRecord(methodSignature, methodHandle); + }) + .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); + } + + /** + * Use Record Class when use jdk 17+ + */ + static class MethodRecord { + String methodSignature; + MethodHandle methodHandle; + + public MethodRecord(String methodSignature, MethodHandle methodHandle) { + this.methodSignature = methodSignature; + this.methodHandle = methodHandle; + } + + public String methodSignature() { + return methodSignature; + } + + public MethodHandle methodHandle() { + return methodHandle; + } + } + + static MethodHandle generateMethodHandle() { return null; } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 856638c..fd55f8c 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -1,13 +1,11 @@ package io.microsphere.redis.spring.metadata; import org.jboss.jandex.MethodInfo; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; import java.lang.invoke.MethodHandle; -import java.lang.reflect.Method; import java.util.List; import java.util.Map; @@ -15,11 +13,11 @@ import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; class RedisCommandsMethodHandlesTest { - private Class redisCommandClass = RedisCommands.class; - static int methodCount = RedisCommands.class.getMethods().length - 1; @Test @@ -31,13 +29,18 @@ void shouldGetAllMethodInfoFromRedisCommand() { .hasSize(methodCount); } - @Disabled @Test - void shouldGetMethodHandleFroMethodInfo() { - Map map = initRedisCommandMethodHandle(); - assertThat(map) - .isNotNull() - .hasSize(methodCount); - assertThatNoException().isThrownBy(RedisCommandsMethodHandles::initRedisCommandMethodHandle); + void shouldGetMethodHandleMapFromMethodInfo() { + try (MockedStatic mockStatic = mockStatic(RedisCommandsMethodHandles.class)) { + MethodHandle mockMethodHandle = mock(MethodHandle.class); + mockStatic.when(RedisCommandsMethodHandles::getAllRedisCommandMethods).thenCallRealMethod(); + mockStatic.when(RedisCommandsMethodHandles::initRedisCommandMethodHandle).thenCallRealMethod(); + mockStatic.when(RedisCommandsMethodHandles::generateMethodHandle).thenReturn(mockMethodHandle); + + Map map = initRedisCommandMethodHandle(); + assertThat(map) + .isNotNull() + .hasSize(methodCount); + } } } From 85d32568c27da7800b7ab5d6822648bce147878e Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 10:57:19 +0800 Subject: [PATCH 05/32] feat: change generateMethodHandle method Signature --- .../metadata/RedisCommandsMethodHandles.java | 4 ++-- .../RedisCommandsMethodHandlesTest.java | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index d4fbe70..051f1e3 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -66,7 +66,7 @@ static Map initRedisCommandMethodHandle() { return methods.stream() .map(methodInfo -> { String methodSignature = methodInfo.toString(); - MethodHandle methodHandle = generateMethodHandle(); + MethodHandle methodHandle = generateMethodHandle(methodInfo); return new MethodRecord(methodSignature, methodHandle); }) .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); @@ -93,7 +93,7 @@ public MethodHandle methodHandle() { } } - static MethodHandle generateMethodHandle() { + static MethodHandle generateMethodHandle(MethodInfo methodInfo) { return null; } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index fd55f8c..e6a0b69 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -1,6 +1,7 @@ package io.microsphere.redis.spring.metadata; import org.jboss.jandex.MethodInfo; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; @@ -9,10 +10,11 @@ import java.util.List; import java.util.Map; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.generateMethodHandle; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -32,10 +34,11 @@ void shouldGetAllMethodInfoFromRedisCommand() { @Test void shouldGetMethodHandleMapFromMethodInfo() { try (MockedStatic mockStatic = mockStatic(RedisCommandsMethodHandles.class)) { - MethodHandle mockMethodHandle = mock(MethodHandle.class); mockStatic.when(RedisCommandsMethodHandles::getAllRedisCommandMethods).thenCallRealMethod(); mockStatic.when(RedisCommandsMethodHandles::initRedisCommandMethodHandle).thenCallRealMethod(); - mockStatic.when(RedisCommandsMethodHandles::generateMethodHandle).thenReturn(mockMethodHandle); + + MethodHandle mockMethodHandle = mock(MethodHandle.class); + mockStatic.when(() -> generateMethodHandle(any(MethodInfo.class))).thenReturn(mockMethodHandle); Map map = initRedisCommandMethodHandle(); assertThat(map) @@ -43,4 +46,13 @@ void shouldGetMethodHandleMapFromMethodInfo() { .hasSize(methodCount); } } + + @Disabled + @Test + void shouldNewMethodHandleInstanceByMethodInfo() { + MethodInfo methodInfo = mock(MethodInfo.class); + MethodHandle methodHandle = generateMethodHandle(methodInfo); + assertThat(methodHandle) + .isNotNull(); + } } From fd6d8d7eecd0a801503352535010d36d63ddcf4b Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 11:19:08 +0800 Subject: [PATCH 06/32] feat: implement findMethodHande --- .../metadata/RedisCommandsMethodHandles.java | 70 +++++++++++++------ .../RedisCommandsMethodHandlesTest.java | 26 +++++-- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 051f1e3..353dcb0 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -2,6 +2,7 @@ import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.MethodParameterInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.connection.RedisCommands; @@ -22,14 +23,24 @@ import java.io.IOException; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import static io.microsphere.util.ClassLoaderUtils.*; + public class RedisCommandsMethodHandles { + private static final Logger logger = LoggerFactory.getLogger(RedisCommandsMethodHandles.class); + + private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup(); + + private static final ClassLoader CURRENT_CLASS_LOADER = RedisCommandsMethodHandles.class.getClassLoader(); + private static List> TARGET_CLASSES; /** @@ -66,35 +77,33 @@ static Map initRedisCommandMethodHandle() { return methods.stream() .map(methodInfo -> { String methodSignature = methodInfo.toString(); - MethodHandle methodHandle = generateMethodHandle(methodInfo); + MethodHandle methodHandle = findMethodHandle(methodInfo); return new MethodRecord(methodSignature, methodHandle); }) .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); } - /** - * Use Record Class when use jdk 17+ - */ - static class MethodRecord { - String methodSignature; - MethodHandle methodHandle; - public MethodRecord(String methodSignature, MethodHandle methodHandle) { - this.methodSignature = methodSignature; - this.methodHandle = methodHandle; - } + static MethodHandle findMethodHandle(MethodInfo methodInfo) { + try { + Class klass = loadClass(methodInfo.declaringClass().name().toString(), CURRENT_CLASS_LOADER); - public String methodSignature() { - return methodSignature; - } + String methodName = methodInfo.name(); - public MethodHandle methodHandle() { - return methodHandle; - } - } + Class returnTypeKlass = loadClass(methodInfo.returnType().toString(), CURRENT_CLASS_LOADER); - static MethodHandle generateMethodHandle(MethodInfo methodInfo) { - return null; + MethodParameterInfo[] array = methodInfo.parameters().toArray(new MethodParameterInfo[]{}); + Class[] parameterKlass = new Class[array.length]; + for (int i = 0; i < array.length; i++) { + parameterKlass[i] = loadClass(array[i].type().toString(), CURRENT_CLASS_LOADER); + } + MethodType methodType = MethodType.methodType(returnTypeKlass, parameterKlass); + + return RedisCommandsMethodHandles.PUBLIC_LOOKUP.findVirtual(klass, methodName, methodType); + } catch (NoSuchMethodException | IllegalAccessException e) { + logger.error("Error occurred when find MethodHandle.\n methodInfo:{}", methodInfo, e); + throw new RuntimeException(e); + } } static { @@ -115,4 +124,25 @@ static MethodHandle generateMethodHandle(MethodInfo methodInfo) { TARGET_CLASSES.add(RedisGeoCommands.class); TARGET_CLASSES.add(RedisHyperLogLogCommands.class); } + + /** + * Use Record Class when use jdk 17+ + */ + static class MethodRecord { + String methodSignature; + MethodHandle methodHandle; + + public MethodRecord(String methodSignature, MethodHandle methodHandle) { + this.methodSignature = methodSignature; + this.methodHandle = methodHandle; + } + + public String methodSignature() { + return methodSignature; + } + + public MethodHandle methodHandle() { + return methodHandle; + } + } } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index e6a0b69..66c612a 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -1,16 +1,21 @@ package io.microsphere.redis.spring.metadata; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; +import org.springframework.data.redis.connection.RedisStringCommands; +import java.io.IOException; import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Map; +import java.util.Optional; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.generateMethodHandle; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.findMethodHandle; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; @@ -38,7 +43,7 @@ void shouldGetMethodHandleMapFromMethodInfo() { mockStatic.when(RedisCommandsMethodHandles::initRedisCommandMethodHandle).thenCallRealMethod(); MethodHandle mockMethodHandle = mock(MethodHandle.class); - mockStatic.when(() -> generateMethodHandle(any(MethodInfo.class))).thenReturn(mockMethodHandle); + mockStatic.when(() -> findMethodHandle(any(MethodInfo.class))).thenReturn(mockMethodHandle); Map map = initRedisCommandMethodHandle(); assertThat(map) @@ -50,9 +55,22 @@ void shouldGetMethodHandleMapFromMethodInfo() { @Disabled @Test void shouldNewMethodHandleInstanceByMethodInfo() { - MethodInfo methodInfo = mock(MethodInfo.class); - MethodHandle methodHandle = generateMethodHandle(methodInfo); + MethodInfo methodInfo = getMethodInfo(); + + MethodHandle methodHandle = findMethodHandle(methodInfo); assertThat(methodHandle) .isNotNull(); + + } + + private static MethodInfo getMethodInfo() { + try { + Index index = Index.of(RedisStringCommands.class); + ClassInfo classInfo = index.getClassByName(RedisStringCommands.class); + Optional setMethod = classInfo.methods().stream().filter(methodInfo -> methodInfo.name().equals("set")).findFirst(); + return setMethod.get(); + } catch (IOException e) { + throw new RuntimeException(e); + } } } From 16b7b28cc0b2f856d7b92f9143353e247c4f17b4 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 11:58:35 +0800 Subject: [PATCH 07/32] build: add jupiter --- microsphere-redis-spring/pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index afef156..03c1d98 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -108,6 +108,9 @@ mockito-inline test - + + org.junit.jupiter + junit-jupiter + \ No newline at end of file From 6ce07bb6147de528606c286b74546cac937ac066 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 11:59:06 +0800 Subject: [PATCH 08/32] feat: getClassBy Method add PrimitiveType --- .../metadata/RedisCommandsMethodHandles.java | 29 ++++++++++++++++ .../RedisCommandsMethodHandlesTest.java | 33 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 353dcb0..537dba5 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -3,6 +3,8 @@ import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; +import org.jboss.jandex.PrimitiveType; +import org.jboss.jandex.Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.connection.RedisCommands; @@ -27,6 +29,8 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -106,6 +110,13 @@ static MethodHandle findMethodHandle(MethodInfo methodInfo) { } } + static Class getClassBy(Type type) { + if (type instanceof PrimitiveType) { + return TypeHelper.PRIMITIVE_TYPE_CLASS_TABLE.get(type.asPrimitiveType().primitive()); + } + return null; + } + static { TARGET_CLASSES = new ArrayList<>(); TARGET_CLASSES.add(RedisCommands.class); @@ -145,4 +156,22 @@ public MethodHandle methodHandle() { return methodHandle; } } + + static class TypeHelper { + private static final EnumMap> PRIMITIVE_TYPE_CLASS_TABLE; + + static { + Map> tmp = new HashMap<>(); + tmp.put(PrimitiveType.Primitive.BOOLEAN, boolean.class); + tmp.put(PrimitiveType.Primitive.BYTE, byte.class); + tmp.put(PrimitiveType.Primitive.SHORT, short.class); + tmp.put(PrimitiveType.Primitive.INT, int.class); + tmp.put(PrimitiveType.Primitive.LONG, long.class); + tmp.put(PrimitiveType.Primitive.FLOAT, float.class); + tmp.put(PrimitiveType.Primitive.DOUBLE, double.class); + tmp.put(PrimitiveType.Primitive.CHAR, char.class); + PRIMITIVE_TYPE_CLASS_TABLE = new EnumMap<>(tmp); + } + + } } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 66c612a..95f0de8 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -3,8 +3,13 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.PrimitiveType; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; import org.springframework.data.redis.connection.RedisStringCommands; @@ -14,11 +19,15 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.findMethodHandle; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Named.named; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -60,6 +69,30 @@ void shouldNewMethodHandleInstanceByMethodInfo() { MethodHandle methodHandle = findMethodHandle(methodInfo); assertThat(methodHandle) .isNotNull(); + } + + @ParameterizedTest(name = "test: {0}") + @MethodSource + void shouldLoadPrimitiveClass(PrimitiveType primitiveType, Class expected) { + Class klass = getClassBy(primitiveType.asPrimitiveType()); + assertThat(klass).isEqualTo(expected); + } + + static Stream shouldLoadPrimitiveClass() { + return Stream.of( + arguments(named("boolean", PrimitiveType.BOOLEAN), boolean.class), + arguments(named("byte", PrimitiveType.BYTE), byte.class), + arguments(named("short", PrimitiveType.SHORT), short.class), + arguments(named("int", PrimitiveType.INT), int.class), + arguments(named("long", PrimitiveType.LONG), long.class), + arguments(named("float", PrimitiveType.FLOAT), float.class), + arguments(named("double", PrimitiveType.DOUBLE), double.class), + arguments(named("char", PrimitiveType.CHAR), char.class) + ); + } + + @Test + void shouldLoadArrayClass() { } From 07c3f4c9a5b5976208b273fb83934eeb8412dd37 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 12:40:56 +0800 Subject: [PATCH 09/32] feat: complete getClassBy Method add ArrayType add VoidType --- .../metadata/RedisCommandsMethodHandles.java | 65 +++++++++++++++---- .../RedisCommandsMethodHandlesTest.java | 35 +++++++++- 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 537dba5..9778460 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -1,10 +1,13 @@ package io.microsphere.redis.spring.metadata; +import org.apache.commons.lang3.StringUtils; +import org.jboss.jandex.ArrayType; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; import org.jboss.jandex.PrimitiveType; import org.jboss.jandex.Type; +import org.jboss.jandex.VoidType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.connection.RedisCommands; @@ -22,6 +25,8 @@ import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.connection.RedisTxCommands; import org.springframework.data.redis.connection.RedisZSetCommands; +import org.springframework.data.redis.connection.stream.RecordId; +import org.springframework.data.redis.connection.stream.StreamOffset; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -33,6 +38,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import static io.microsphere.util.ClassLoaderUtils.*; @@ -111,9 +118,36 @@ static MethodHandle findMethodHandle(MethodInfo methodInfo) { } static Class getClassBy(Type type) { + if (type instanceof VoidType) { + return void.class; + } + if (type instanceof PrimitiveType) { return TypeHelper.PRIMITIVE_TYPE_CLASS_TABLE.get(type.asPrimitiveType().primitive()); } + + if (type instanceof ArrayType) { + ArrayType arrayType = type.asArrayType(); + // NOTE: arrayType.elementType().name() + // example java.lang.String + // when use jdk21 local() value is "String" prefix is "java.lang" + // when use jdk8 local() value is "java.lang.String" prefix is null + String local = arrayType.elementType().name().local(); + String elementType; + if (local.lastIndexOf(".") != -1) { + elementType = local.substring(local.lastIndexOf(".") + 1); + } else { + elementType = local; + } + // NOTE: use String.repeat() when use jdk11+ and remove Apache commons lang3 StringUtils dependency + String repeat = StringUtils.repeat("[]", arrayType.dimensions()); + Class klass = TypeHelper.ARRAY_TYPE_CLASS_TABLE.get(elementType + repeat); + + if (Objects.isNull(klass)) { + throw new RuntimeException("need to add Class"); + } + return klass; + } return null; } @@ -137,7 +171,7 @@ static Class getClassBy(Type type) { } /** - * Use Record Class when use jdk 17+ + * NOTE: Use Record Class when use jdk 17+ */ static class MethodRecord { String methodSignature; @@ -158,19 +192,26 @@ public MethodHandle methodHandle() { } static class TypeHelper { - private static final EnumMap> PRIMITIVE_TYPE_CLASS_TABLE; + private static final EnumMap> PRIMITIVE_TYPE_CLASS_TABLE = new EnumMap<>(PrimitiveType.Primitive.class); + private static final Map> ARRAY_TYPE_CLASS_TABLE = new HashMap<>(); static { - Map> tmp = new HashMap<>(); - tmp.put(PrimitiveType.Primitive.BOOLEAN, boolean.class); - tmp.put(PrimitiveType.Primitive.BYTE, byte.class); - tmp.put(PrimitiveType.Primitive.SHORT, short.class); - tmp.put(PrimitiveType.Primitive.INT, int.class); - tmp.put(PrimitiveType.Primitive.LONG, long.class); - tmp.put(PrimitiveType.Primitive.FLOAT, float.class); - tmp.put(PrimitiveType.Primitive.DOUBLE, double.class); - tmp.put(PrimitiveType.Primitive.CHAR, char.class); - PRIMITIVE_TYPE_CLASS_TABLE = new EnumMap<>(tmp); + // NOTE: use new EnumMap(Map.of()) when use jdk11+ + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.BOOLEAN, boolean.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.BYTE, byte.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.SHORT, short.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.INT, int.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.LONG, long.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.FLOAT, float.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.DOUBLE, double.class); + PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.CHAR, char.class); + + ARRAY_TYPE_CLASS_TABLE.put("byte[]", byte[].class); + ARRAY_TYPE_CLASS_TABLE.put("byte[][]", byte[][].class); + ARRAY_TYPE_CLASS_TABLE.put("int[]", int[].class); + ARRAY_TYPE_CLASS_TABLE.put("String[]", String[].class); + ARRAY_TYPE_CLASS_TABLE.put("RecordId[]", RecordId[].class); + ARRAY_TYPE_CLASS_TABLE.put("StreamOffset[]", StreamOffset[].class); } } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 95f0de8..c992a2f 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -1,9 +1,13 @@ package io.microsphere.redis.spring.metadata; +import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.PrimitiveType; +import org.jboss.jandex.Type; +import org.jboss.jandex.VoidType; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; @@ -13,6 +17,8 @@ import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.connection.stream.RecordId; +import org.springframework.data.redis.connection.stream.StreamOffset; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -26,6 +32,10 @@ import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; +import static org.jboss.jandex.ArrayType.builder; +import static org.jboss.jandex.DotName.createSimple; +import static org.jboss.jandex.Type.Kind.CLASS; +import static org.jboss.jandex.Type.create; import static org.junit.jupiter.api.Named.named; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; @@ -74,7 +84,7 @@ void shouldNewMethodHandleInstanceByMethodInfo() { @ParameterizedTest(name = "test: {0}") @MethodSource void shouldLoadPrimitiveClass(PrimitiveType primitiveType, Class expected) { - Class klass = getClassBy(primitiveType.asPrimitiveType()); + Class klass = getClassBy(primitiveType); assertThat(klass).isEqualTo(expected); } @@ -91,9 +101,28 @@ static Stream shouldLoadPrimitiveClass() { ); } - @Test - void shouldLoadArrayClass() { + @ParameterizedTest(name = "test: {0}") + @MethodSource + void shouldLoadArrayClass(ArrayType arrayType, Class expected) { + Class klass = getClassBy(arrayType); + assertThat(klass).isEqualTo(expected); + } + static Stream shouldLoadArrayClass() { + return Stream.of( + arguments(named("byte[]", builder(PrimitiveType.BYTE, 1).build()), byte[].class), + arguments(named("byte[][]", builder(PrimitiveType.BYTE, 2).build()), byte[][].class), + arguments(named("int[]", builder(PrimitiveType.INT, 1).build()), int[].class), + arguments(named("String[]", builder(create(createSimple(String.class), CLASS), 1).build()), String[].class), + arguments(named("RecordId[]", builder(create(createSimple(RecordId.class), CLASS), 1).build()), RecordId[].class), + arguments(named("StreamOffset[]", builder(create(createSimple(StreamOffset.class), CLASS), 1).build()), StreamOffset[].class) + ); + } + + @Test + void shouldVoidClass() { + Class klass = getClassBy(VoidType.VOID); + assertThat(klass).isEqualTo(void.class); } private static MethodInfo getMethodInfo() { From 16ad659a4771dbda712cabca2754b038871bd0ea Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 12:44:29 +0800 Subject: [PATCH 10/32] doc: modify comment --- .../redis/spring/metadata/RedisCommandsMethodHandles.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 9778460..ebd6ac7 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -130,8 +130,8 @@ static Class getClassBy(Type type) { ArrayType arrayType = type.asArrayType(); // NOTE: arrayType.elementType().name() // example java.lang.String - // when use jdk21 local() value is "String" prefix is "java.lang" - // when use jdk8 local() value is "java.lang.String" prefix is null + // when use jdk21 local() value is "String" prefix() is "java.lang" + // when use jdk8 local() value is "java.lang.String" prefix() is null String local = arrayType.elementType().name().local(); String elementType; if (local.lastIndexOf(".") != -1) { @@ -196,7 +196,7 @@ static class TypeHelper { private static final Map> ARRAY_TYPE_CLASS_TABLE = new HashMap<>(); static { - // NOTE: use new EnumMap(Map.of()) when use jdk11+ + // NOTE: use new EnumMap(Map.of()) to simplify the code when use jdk11+ PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.BOOLEAN, boolean.class); PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.BYTE, byte.class); PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.SHORT, short.class); From 52891e559e6044bc42d94a1e7a749cc7860db91e Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 13:18:31 +0800 Subject: [PATCH 11/32] feat: complete getClassBy Method add ParameterizedType add ClassType --- .../metadata/RedisCommandsMethodHandles.java | 7 +++-- .../RedisCommandsMethodHandlesTest.java | 29 ++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index ebd6ac7..62037e9 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -1,5 +1,6 @@ package io.microsphere.redis.spring.metadata; +import io.microsphere.util.ClassLoaderUtils; import org.apache.commons.lang3.StringUtils; import org.jboss.jandex.ArrayType; import org.jboss.jandex.Index; @@ -39,10 +40,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.stream.Collectors; -import static io.microsphere.util.ClassLoaderUtils.*; +import static io.microsphere.util.ClassLoaderUtils.loadClass; public class RedisCommandsMethodHandles { @@ -148,7 +148,8 @@ static Class getClassBy(Type type) { } return klass; } - return null; + + return ClassLoaderUtils.loadClass(type.name().toString(), CURRENT_CLASS_LOADER); } static { diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index c992a2f..6662c13 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -2,14 +2,13 @@ import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.DotName; +import org.jboss.jandex.ClassType; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.PrimitiveType; -import org.jboss.jandex.Type; import org.jboss.jandex.VoidType; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -83,12 +82,12 @@ void shouldNewMethodHandleInstanceByMethodInfo() { @ParameterizedTest(name = "test: {0}") @MethodSource - void shouldLoadPrimitiveClass(PrimitiveType primitiveType, Class expected) { + void shouldGetClassWhenTypeIsPrimitiveClass(PrimitiveType primitiveType, Class expected) { Class klass = getClassBy(primitiveType); assertThat(klass).isEqualTo(expected); } - static Stream shouldLoadPrimitiveClass() { + static Stream shouldGetClassWhenTypeIsPrimitiveClass() { return Stream.of( arguments(named("boolean", PrimitiveType.BOOLEAN), boolean.class), arguments(named("byte", PrimitiveType.BYTE), byte.class), @@ -103,12 +102,12 @@ static Stream shouldLoadPrimitiveClass() { @ParameterizedTest(name = "test: {0}") @MethodSource - void shouldLoadArrayClass(ArrayType arrayType, Class expected) { + void shouldGetClassWhenTypeIsArrayClass(ArrayType arrayType, Class expected) { Class klass = getClassBy(arrayType); assertThat(klass).isEqualTo(expected); } - static Stream shouldLoadArrayClass() { + static Stream shouldGetClassWhenTypeIsArrayClass() { return Stream.of( arguments(named("byte[]", builder(PrimitiveType.BYTE, 1).build()), byte[].class), arguments(named("byte[][]", builder(PrimitiveType.BYTE, 2).build()), byte[][].class), @@ -120,11 +119,25 @@ static Stream shouldLoadArrayClass() { } @Test - void shouldVoidClass() { + void shouldGetVoidClass() { Class klass = getClassBy(VoidType.VOID); assertThat(klass).isEqualTo(void.class); } + @Test + void shouldGetClassWhenTypeIsParameterizedType() { + ParameterizedType parameterizedType = ParameterizedType.builder(List.class).addArgument(ClassType.create(String.class)).build(); + Class klass = getClassBy(parameterizedType); + assertThat(klass).isEqualTo(List.class); + } + + @Test + void shouldGetClassWhenTypeIsClassType() { + ClassType classType = ClassType.create(Object.class); + Class klass = getClassBy(classType); + assertThat(klass).isEqualTo(Object.class); + } + private static MethodInfo getMethodInfo() { try { Index index = Index.of(RedisStringCommands.class); From 76929c709e3da786b9a5efa07e60a7868855fd7a Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 13:34:56 +0800 Subject: [PATCH 12/32] feat: complete findMethodHandle --- .../redis/spring/metadata/RedisCommandsMethodHandles.java | 7 ++++--- .../spring/metadata/RedisCommandsMethodHandlesTest.java | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 62037e9..00525ea 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -3,6 +3,7 @@ import io.microsphere.util.ClassLoaderUtils; import org.apache.commons.lang3.StringUtils; import org.jboss.jandex.ArrayType; +import org.jboss.jandex.ClassType; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; @@ -97,16 +98,16 @@ static Map initRedisCommandMethodHandle() { static MethodHandle findMethodHandle(MethodInfo methodInfo) { try { - Class klass = loadClass(methodInfo.declaringClass().name().toString(), CURRENT_CLASS_LOADER); + Class klass = getClassBy(ClassType.create(methodInfo.declaringClass().name())); String methodName = methodInfo.name(); - Class returnTypeKlass = loadClass(methodInfo.returnType().toString(), CURRENT_CLASS_LOADER); + Class returnTypeKlass = getClassBy(methodInfo.returnType()); MethodParameterInfo[] array = methodInfo.parameters().toArray(new MethodParameterInfo[]{}); Class[] parameterKlass = new Class[array.length]; for (int i = 0; i < array.length; i++) { - parameterKlass[i] = loadClass(array[i].type().toString(), CURRENT_CLASS_LOADER); + parameterKlass[i] = getClassBy(array[i].type()); } MethodType methodType = MethodType.methodType(returnTypeKlass, parameterKlass); diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 6662c13..9a9b88a 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -70,7 +70,6 @@ void shouldGetMethodHandleMapFromMethodInfo() { } } - @Disabled @Test void shouldNewMethodHandleInstanceByMethodInfo() { MethodInfo methodInfo = getMethodInfo(); From 89b81ed13ad20f7fe262c9490f0b5d975c9a7b02 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 13:42:55 +0800 Subject: [PATCH 13/32] refactor: simplify the code --- .../metadata/RedisCommandsMethodHandles.java | 44 ++++++++++--------- .../RedisCommandsMethodHandlesTest.java | 7 ++- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 00525ea..4db4cc4 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -84,33 +84,21 @@ static List getAllRedisCommandMethods() { } static Map initRedisCommandMethodHandle() { - List methods = getAllRedisCommandMethods(); - - return methods.stream() - .map(methodInfo -> { - String methodSignature = methodInfo.toString(); - MethodHandle methodHandle = findMethodHandle(methodInfo); - return new MethodRecord(methodSignature, methodHandle); - }) + + return getAllRedisCommandMethods() + .stream() + .map(methodInfo -> new MethodRecord(methodInfo.toString(), findMethodHandle(methodInfo))) .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); } static MethodHandle findMethodHandle(MethodInfo methodInfo) { - try { - Class klass = getClassBy(ClassType.create(methodInfo.declaringClass().name())); + Class klass = getClassBy(ClassType.create(methodInfo.declaringClass().name())); - String methodName = methodInfo.name(); - - Class returnTypeKlass = getClassBy(methodInfo.returnType()); - - MethodParameterInfo[] array = methodInfo.parameters().toArray(new MethodParameterInfo[]{}); - Class[] parameterKlass = new Class[array.length]; - for (int i = 0; i < array.length; i++) { - parameterKlass[i] = getClassBy(array[i].type()); - } - MethodType methodType = MethodType.methodType(returnTypeKlass, parameterKlass); + String methodName = methodInfo.name(); + MethodType methodType = getMethodType(methodInfo); + try { return RedisCommandsMethodHandles.PUBLIC_LOOKUP.findVirtual(klass, methodName, methodType); } catch (NoSuchMethodException | IllegalAccessException e) { logger.error("Error occurred when find MethodHandle.\n methodInfo:{}", methodInfo, e); @@ -118,6 +106,19 @@ static MethodHandle findMethodHandle(MethodInfo methodInfo) { } } + private static MethodType getMethodType(MethodInfo methodInfo) { + Class returnTypeKlass = getClassBy(methodInfo.returnType()); + + MethodParameterInfo[] array = methodInfo.parameters().toArray(new MethodParameterInfo[]{}); + Class[] parameterKlass = new Class[array.length]; + for (int i = 0; i < array.length; i++) { + parameterKlass[i] = getClassBy(array[i].type()); + } + + MethodType methodType = MethodType.methodType(returnTypeKlass, parameterKlass); + return methodType; + } + static Class getClassBy(Type type) { if (type instanceof VoidType) { return void.class; @@ -197,6 +198,9 @@ static class TypeHelper { private static final EnumMap> PRIMITIVE_TYPE_CLASS_TABLE = new EnumMap<>(PrimitiveType.Primitive.class); private static final Map> ARRAY_TYPE_CLASS_TABLE = new HashMap<>(); + private TypeHelper() { + } + static { // NOTE: use new EnumMap(Map.of()) to simplify the code when use jdk11+ PRIMITIVE_TYPE_CLASS_TABLE.put(PrimitiveType.Primitive.BOOLEAN, boolean.class); diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 9a9b88a..d897d94 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -56,6 +56,7 @@ void shouldGetAllMethodInfoFromRedisCommand() { @Test void shouldGetMethodHandleMapFromMethodInfo() { + /* try (MockedStatic mockStatic = mockStatic(RedisCommandsMethodHandles.class)) { mockStatic.when(RedisCommandsMethodHandles::getAllRedisCommandMethods).thenCallRealMethod(); mockStatic.when(RedisCommandsMethodHandles::initRedisCommandMethodHandle).thenCallRealMethod(); @@ -67,7 +68,11 @@ void shouldGetMethodHandleMapFromMethodInfo() { assertThat(map) .isNotNull() .hasSize(methodCount); - } + }*/ + Map map = initRedisCommandMethodHandle(); + assertThat(map) + .isNotNull() + .hasSize(methodCount); } @Test From c8fa4e02f2a05900d0c49e6c4031531b57ce5332 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 14:18:37 +0800 Subject: [PATCH 14/32] feat: find MethodHandle From Map add method getMethodHandleBy add MethodHandleNotFoundException when not found --- .../metadata/RedisCommandsMethodHandles.java | 34 +++++++++++---- .../MethodHandleNotFoundException.java | 14 +++++++ .../RedisCommandsMethodHandlesTest.java | 41 +++++++++++-------- 3 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 4db4cc4..9908121 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -1,5 +1,6 @@ package io.microsphere.redis.spring.metadata; +import io.microsphere.redis.spring.metadata.exception.MethodHandleNotFoundException; import io.microsphere.util.ClassLoaderUtils; import org.apache.commons.lang3.StringUtils; import org.jboss.jandex.ArrayType; @@ -43,8 +44,6 @@ import java.util.Objects; import java.util.stream.Collectors; -import static io.microsphere.util.ClassLoaderUtils.loadClass; - public class RedisCommandsMethodHandles { private static final Logger logger = LoggerFactory.getLogger(RedisCommandsMethodHandles.class); @@ -53,7 +52,27 @@ public class RedisCommandsMethodHandles { private static final ClassLoader CURRENT_CLASS_LOADER = RedisCommandsMethodHandles.class.getClassLoader(); - private static List> TARGET_CLASSES; + private static final List> TARGET_CLASSES; + private static final Map METHOD_HANDLE_MAP; + + private RedisCommandsMethodHandles() { + } + + /** + * find MethodHandle from METHOD_HANDLE_MAP + * + * @param methodSignature {@link MethodInfo#toString()} + * @return a MethodHandle + * @throws MethodHandleNotFoundException + */ + public static MethodHandle getMethodHandleBy(String methodSignature) throws MethodHandleNotFoundException { + MethodHandle methodHandle = METHOD_HANDLE_MAP.get(methodSignature); + if (Objects.isNull(methodHandle)) { + logger.error("can't find MethodHandle from RedisCommands methodSignature:{}", methodSignature); + throw new MethodHandleNotFoundException("can't find MethodHandle from RedisCommands", methodSignature); + } + return methodHandle; + } /** * get all public methods from {@link RedisCommands}
    @@ -62,7 +81,7 @@ public class RedisCommandsMethodHandles { *
  • lambda$xDel$1
  • *
  • lambda$xAck$0
  • * - * @return + * @return List of RedisCommands all MethodInfo(include super interface) */ static List getAllRedisCommandMethods() { try { @@ -91,7 +110,6 @@ static Map initRedisCommandMethodHandle() { .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); } - static MethodHandle findMethodHandle(MethodInfo methodInfo) { Class klass = getClassBy(ClassType.create(methodInfo.declaringClass().name())); @@ -115,8 +133,7 @@ private static MethodType getMethodType(MethodInfo methodInfo) { parameterKlass[i] = getClassBy(array[i].type()); } - MethodType methodType = MethodType.methodType(returnTypeKlass, parameterKlass); - return methodType; + return MethodType.methodType(returnTypeKlass, parameterKlass); } static Class getClassBy(Type type) { @@ -155,6 +172,7 @@ static Class getClassBy(Type type) { } static { + // NOTE: use List.of() to simplify the initial logic TARGET_CLASSES = new ArrayList<>(); TARGET_CLASSES.add(RedisCommands.class); TARGET_CLASSES.add(RedisKeyCommands.class); @@ -171,6 +189,8 @@ static Class getClassBy(Type type) { TARGET_CLASSES.add(RedisScriptingCommands.class); TARGET_CLASSES.add(RedisGeoCommands.class); TARGET_CLASSES.add(RedisHyperLogLogCommands.class); + + METHOD_HANDLE_MAP = initRedisCommandMethodHandle(); } /** diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java new file mode 100644 index 0000000..b1b2655 --- /dev/null +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java @@ -0,0 +1,14 @@ +package io.microsphere.redis.spring.metadata.exception; + +public class MethodHandleNotFoundException extends RuntimeException { + + private final String methodSignature; + public MethodHandleNotFoundException(String message, String methodSignature) { + super(message); + this.methodSignature = methodSignature; + } + + public String getMethodSignature() { + return methodSignature; + } +} diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index d897d94..093f791 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -1,5 +1,6 @@ package io.microsphere.redis.spring.metadata; +import io.microsphere.redis.spring.metadata.exception.MethodHandleNotFoundException; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; @@ -8,12 +9,10 @@ import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.PrimitiveType; import org.jboss.jandex.VoidType; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.MockedStatic; import org.springframework.data.redis.connection.RedisCommands; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.connection.stream.RecordId; @@ -29,17 +28,16 @@ import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.findMethodHandle; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowableOfType; import static org.jboss.jandex.ArrayType.builder; import static org.jboss.jandex.DotName.createSimple; import static org.jboss.jandex.Type.Kind.CLASS; import static org.jboss.jandex.Type.create; import static org.junit.jupiter.api.Named.named; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; class RedisCommandsMethodHandlesTest { @@ -56,19 +54,6 @@ void shouldGetAllMethodInfoFromRedisCommand() { @Test void shouldGetMethodHandleMapFromMethodInfo() { - /* - try (MockedStatic mockStatic = mockStatic(RedisCommandsMethodHandles.class)) { - mockStatic.when(RedisCommandsMethodHandles::getAllRedisCommandMethods).thenCallRealMethod(); - mockStatic.when(RedisCommandsMethodHandles::initRedisCommandMethodHandle).thenCallRealMethod(); - - MethodHandle mockMethodHandle = mock(MethodHandle.class); - mockStatic.when(() -> findMethodHandle(any(MethodInfo.class))).thenReturn(mockMethodHandle); - - Map map = initRedisCommandMethodHandle(); - assertThat(map) - .isNotNull() - .hasSize(methodCount); - }*/ Map map = initRedisCommandMethodHandle(); assertThat(map) .isNotNull() @@ -84,6 +69,26 @@ void shouldNewMethodHandleInstanceByMethodInfo() { .isNotNull(); } + @Test + void shouldGetMethodHandleByMethodSignature() { + String methodSignature = getMethodInfo().toString(); + MethodHandle methodHandle = getMethodHandleBy(methodSignature); + assertThat(methodHandle) + .isNotNull(); + } + + @Test + void shouldThrowMethodHandleNotFoundExceptionWhenMiss() { + MethodHandleNotFoundException missingMethodSignature = catchThrowableOfType( + () -> getMethodHandleBy("MissingMethodSignature"), + MethodHandleNotFoundException.class); + + assertThat(missingMethodSignature) + .hasMessage("can't find MethodHandle from RedisCommands"); + assertThat(missingMethodSignature.getMethodSignature()) + .isEqualTo("MissingMethodSignature"); + } + @ParameterizedTest(name = "test: {0}") @MethodSource void shouldGetClassWhenTypeIsPrimitiveClass(PrimitiveType primitiveType, Class expected) { From b9ad5bf5b39a23319f05aa5dcdf98cafd5ccab4e Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 15:34:03 +0800 Subject: [PATCH 15/32] build: add testcontainers Dependency add testcontainers in the test scope add spring-boot-starter-data-redis in the test scope to autoconfigure in test --- microsphere-redis-parent/pom.xml | 10 ++++++++++ microsphere-redis-replicator-spring/pom.xml | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/microsphere-redis-parent/pom.xml b/microsphere-redis-parent/pom.xml index e8a862c..350ea65 100644 --- a/microsphere-redis-parent/pom.xml +++ b/microsphere-redis-parent/pom.xml @@ -29,6 +29,7 @@ 2021.0.5.0 2021.0.7 3.1.6 + 1.19.5 @@ -114,6 +115,15 @@ jandex ${jandex.version} + + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + diff --git a/microsphere-redis-replicator-spring/pom.xml b/microsphere-redis-replicator-spring/pom.xml index 6e12a2d..08d6ab7 100644 --- a/microsphere-redis-replicator-spring/pom.xml +++ b/microsphere-redis-replicator-spring/pom.xml @@ -92,6 +92,22 @@ test + + org.springframework.boot + spring-boot-starter-data-redis + test + + + + org.testcontainers + testcontainers + test + + + org.testcontainers + junit-jupiter + test + \ No newline at end of file From 1f940eea82da0d891499c9654262ec421ef54699 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 16:54:26 +0800 Subject: [PATCH 16/32] feat: exception message show methodSignature --- .../metadata/exception/MethodHandleNotFoundException.java | 4 +++- .../redis/spring/metadata/RedisCommandsMethodHandlesTest.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java index b1b2655..0fae483 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/exception/MethodHandleNotFoundException.java @@ -3,12 +3,14 @@ public class MethodHandleNotFoundException extends RuntimeException { private final String methodSignature; + public MethodHandleNotFoundException(String message, String methodSignature) { - super(message); + super(message + ", methodSignature is " + methodSignature); this.methodSignature = methodSignature; } public String getMethodSignature() { return methodSignature; } + } diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 093f791..8a2144d 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -84,7 +84,8 @@ void shouldThrowMethodHandleNotFoundExceptionWhenMiss() { MethodHandleNotFoundException.class); assertThat(missingMethodSignature) - .hasMessage("can't find MethodHandle from RedisCommands"); + .hasMessageContaining("can't find MethodHandle from RedisCommands") + .hasMessageContaining("methodSignature"); assertThat(missingMethodSignature.getMethodSignature()) .isEqualTo("MissingMethodSignature"); } From ec8205b43ea9300961ad2bdb4e49aebdfb9fb899 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 16:59:55 +0800 Subject: [PATCH 17/32] build: transfer jandex to dependent model --- microsphere-redis-spring/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index 03c1d98..299b2bb 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -69,7 +69,6 @@ io.smallrye jandex - true From 563db9bc46cb26e62177490913be3e29502de727 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 17:02:00 +0800 Subject: [PATCH 18/32] test: write event handle test case --- ...HandleRedisCommandReplicatedEventTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java new file mode 100644 index 0000000..552455c --- /dev/null +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -0,0 +1,109 @@ +package io.microsphere.redis.replicator.spring; + +import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; +import io.microsphere.redis.spring.event.RedisCommandEvent; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.lang.reflect.Method; + +import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.findWriteCommandMethod; +import static io.microsphere.redis.spring.serializer.RedisCommandEventSerializer.VERSION_V1; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.utility.DockerImageName.parse; + +@Testcontainers +class HandleRedisCommandReplicatedEventTest { + @Container + static GenericContainer redisContainer = new GenericContainer<>(parse("redis:latest")).withExposedPorts(6379); + + @DynamicPropertySource + static void redisProperties(DynamicPropertyRegistry registry) { + + System.out.println(registry); + registry.add("spring.redis.host", () -> redisContainer.getHost()); + registry.add("spring.redis.port", () -> redisContainer.getMappedPort(6379)); + } + + @Nested + @SpringJUnitConfig(classes = { + RedisCommandReplicator.class, + RedisAutoConfiguration.class + }) + @TestPropertySource(properties = { + "microsphere.redis.replicator.consumer.event.handler=REFLECT" + }) + class ReflectEventHandleTest { + @Autowired + ApplicationContext applicationContext; + + @Autowired + RedisTemplate redisTemplate; + + @Test + void invokeMethodByReflect() { + String key = "Reflect"; + String expected = "Reflect"; + RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); + applicationContext.publishEvent(redisCommandReplicatedEvent); + + String value = redisTemplate.opsForValue().get(key); + assertThat(value).isEqualTo(expected); + } + } + + @Nested + @SpringJUnitConfig(classes = { + RedisCommandReplicator.class, + RedisAutoConfiguration.class + }) + @TestPropertySource(properties = { + "microsphere.redis.replicator.consumer.event.handler=METHOD_HANDLE" + }) + class MethodHandleEventHandleTest { + + @Autowired + ApplicationContext applicationContext; + + @Autowired + RedisTemplate redisTemplate; + + @Test + void invokeMethodByMethodHandle() { + String key = "MethodHandle"; + String expected = "MethodHandle"; + RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); + applicationContext.publishEvent(redisCommandReplicatedEvent); + + + String value = redisTemplate.opsForValue().get(key); + assertThat(value).isEqualTo(expected); + } + + + } + + + private static RedisCommandReplicatedEvent getRedisCommandReplicatedEvent(String key, String value) { + String interfaceName = "org.springframework.data.redis.connection.RedisStringCommands"; + String methodName = "set"; + String[] parameterTypes = new String[]{"[B", "[B"}; + Method method = findWriteCommandMethod(interfaceName, methodName, parameterTypes); + + RedisCommandEvent redisCommandEvent = RedisCommandEvent.Builder.source("test").applicationName("test-application").method(method).args(key.getBytes(), value.getBytes()).serializationVersion(VERSION_V1).build(); + + return new RedisCommandReplicatedEvent(redisCommandEvent, "domain"); + } + +} From f4ed19c79756017ec5521036ddff2327b2ae4c18 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 17 Mar 2024 17:02:44 +0800 Subject: [PATCH 19/32] refactor: extract event handle interface --- .../spring/RedisCommandReplicator.java | 32 ++++++++++++++++--- .../config/RedisReplicatorConfiguration.java | 3 +- ...dleRedisCommandReplicatedEventHandler.java | 27 ++++++++++++++++ .../RedisCommandReplicatedEventHandler.java | 14 ++++++++ ...ectRedisCommandReplicatedEventHandler.java | 21 ++++++++++++ .../main/resources/META-INF/spring.factories | 6 +++- 6 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java create mode 100644 microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/RedisCommandReplicatedEventHandler.java create mode 100644 microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/ReflectRedisCommandReplicatedEventHandler.java diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/RedisCommandReplicator.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/RedisCommandReplicator.java index 2bbaa5c..15e2471 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/RedisCommandReplicator.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/RedisCommandReplicator.java @@ -1,17 +1,23 @@ package io.microsphere.redis.replicator.spring; -import io.microsphere.redis.spring.event.RedisCommandEvent; import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; +import io.microsphere.redis.replicator.spring.event.handler.RedisCommandReplicatedEventHandler; +import io.microsphere.redis.spring.event.RedisCommandEvent; +import io.microsphere.spring.util.SpringFactoriesLoaderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.function.Function; +import static io.microsphere.redis.replicator.spring.config.RedisReplicatorConfiguration.CONSUMER_EVENT_HANDLE_PROPERTY_NAME; +import static io.microsphere.redis.replicator.spring.event.handler.RedisCommandReplicatedEventHandler.EventHandleName.REFLECT; import static io.microsphere.redis.spring.beans.Wrapper.tryUnwrap; import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.findWriteCommandMethod; import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.getRedisCommandBindingFunction; @@ -23,7 +29,8 @@ * @author Mercy * @since 1.0.0 */ -public class RedisCommandReplicator implements ApplicationListener { +public class RedisCommandReplicator implements ApplicationListener, + ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(RedisCommandReplicator.class); @@ -35,6 +42,7 @@ public RedisCommandReplicator(RedisConnectionFactory redisConnectionFactory) { this.redisConnectionFactory = tryUnwrap(redisConnectionFactory, RedisConnectionFactory.class); } + @Override public void onApplicationEvent(RedisCommandReplicatedEvent event) { try { @@ -53,12 +61,26 @@ private void handleRedisCommandEvent(RedisCommandReplicatedEvent event) throws T Object[] args = redisCommandEvent.getArgs(); Function bindingFunction = getRedisCommandBindingFunction(interfaceNme); Object redisCommandObject = bindingFunction.apply(redisConnection); - // TODO: Native method implementation - ReflectionUtils.invokeMethod(method, redisCommandObject, args); + // Native method implementation + // ReflectionUtils.invokeMethod(method, redisCommandObject, args); + eventHandler.handleEvent(method, redisCommandObject, args); } } private RedisConnection getRedisConnection() { return redisConnectionFactory.getConnection(); } + + RedisCommandReplicatedEventHandler eventHandler; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + String eventHandleType = applicationContext.getEnvironment() + .getProperty(CONSUMER_EVENT_HANDLE_PROPERTY_NAME, String.class, REFLECT.name()); + eventHandler = SpringFactoriesLoaderUtils.loadFactories(applicationContext, RedisCommandReplicatedEventHandler.class) + .stream() + .filter(eventHandle -> eventHandle.name().equals(eventHandleType)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No eventHandle found")); + } } diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/config/RedisReplicatorConfiguration.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/config/RedisReplicatorConfiguration.java index b7859b1..f98ac97 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/config/RedisReplicatorConfiguration.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/config/RedisReplicatorConfiguration.java @@ -63,10 +63,11 @@ public class RedisReplicatorConfiguration implements ApplicationListener Date: Sun, 17 Mar 2024 17:31:29 +0800 Subject: [PATCH 20/32] feat: transferMethodToMethodSignature unImplement --- ...thodHandleRedisCommandReplicatedEventHandler.java | 6 +++--- .../HandleRedisCommandReplicatedEventTest.java | 2 ++ .../spring/metadata/RedisCommandsMethodHandles.java | 8 ++++++++ .../metadata/RedisCommandsMethodHandlesTest.java | 12 ++++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java index 0bbb9e5..2ccdc5b 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java @@ -1,16 +1,16 @@ package io.microsphere.redis.replicator.spring.event.handler; import io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles; -import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; + +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; public class MethodHandleRedisCommandReplicatedEventHandler implements RedisCommandReplicatedEventHandler { @Override public void handleEvent(Method method, Object redisCommandObject, Object... args) throws Throwable { - String methodSignature = ""; + String methodSignature = transferMethodToMethodSignature(method); int length = args.length; Object[] arguments = new Object[1 + args.length]; args[0] = redisCommandObject; diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index 552455c..215a702 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -2,6 +2,7 @@ import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; import io.microsphere.redis.spring.event.RedisCommandEvent; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -79,6 +80,7 @@ class MethodHandleEventHandleTest { @Autowired RedisTemplate redisTemplate; + @Disabled @Test void invokeMethodByMethodHandle() { String key = "MethodHandle"; diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 9908121..8f9992a 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -35,6 +35,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.EnumMap; @@ -193,6 +194,13 @@ static Class getClassBy(Type type) { METHOD_HANDLE_MAP = initRedisCommandMethodHandle(); } + public static String transferMethodToMethodSignature(Method method) { + // TODO 我写不出来了 + // example RedisStringCommands#set()的方法签名: + // target java.lang.Boolean set(byte[] key, byte[] value); + return ""; + } + /** * NOTE: Use Record Class when use jdk 17+ */ diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 8a2144d..ca1a3bd 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Optional; @@ -30,6 +31,7 @@ import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowableOfType; import static org.jboss.jandex.ArrayType.builder; @@ -90,6 +92,16 @@ void shouldThrowMethodHandleNotFoundExceptionWhenMiss() { .isEqualTo("MissingMethodSignature"); } + @Test + void shouldTransferMethodToMethodHandleSignature() throws NoSuchMethodException { + MethodInfo methodInfo = getMethodInfo(); + Method setMethod = RedisStringCommands.class.getMethod("set", byte[].class, byte[].class); + + String signature = transferMethodToMethodSignature(setMethod); + assertThat(signature) + .isEqualTo(methodInfo.toString()); + } + @ParameterizedTest(name = "test: {0}") @MethodSource void shouldGetClassWhenTypeIsPrimitiveClass(PrimitiveType primitiveType, Class expected) { From f24a3a01875e9e426c14e536a98b47b0e4a720fd Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Mon, 18 Mar 2024 07:44:56 +0800 Subject: [PATCH 21/32] test: MethodHandleRedisCommandReplicatedEventHandler mock unimplemented --- microsphere-redis-replicator-spring/pom.xml | 5 ++++ ...dleRedisCommandReplicatedEventHandler.java | 2 +- ...HandleRedisCommandReplicatedEventTest.java | 30 +++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/microsphere-redis-replicator-spring/pom.xml b/microsphere-redis-replicator-spring/pom.xml index 08d6ab7..f989028 100644 --- a/microsphere-redis-replicator-spring/pom.xml +++ b/microsphere-redis-replicator-spring/pom.xml @@ -108,6 +108,11 @@ junit-jupiter test + + org.mockito + mockito-inline + test + \ No newline at end of file diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java index 2ccdc5b..e5f0fba 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java @@ -13,7 +13,7 @@ public void handleEvent(Method method, Object redisCommandObject, Object... args String methodSignature = transferMethodToMethodSignature(method); int length = args.length; Object[] arguments = new Object[1 + args.length]; - args[0] = redisCommandObject; + arguments[0] = redisCommandObject; for (int i = 0; i < length; i++) { arguments[i + 1] = args[i]; } diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index 215a702..3a0e787 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -2,12 +2,15 @@ import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; import io.microsphere.redis.spring.event.RedisCommandEvent; +import io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -17,11 +20,16 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import java.io.IOException; import java.lang.reflect.Method; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.findWriteCommandMethod; import static io.microsphere.redis.spring.serializer.RedisCommandEventSerializer.VERSION_V1; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.testcontainers.utility.DockerImageName.parse; @Testcontainers @@ -83,14 +91,24 @@ class MethodHandleEventHandleTest { @Disabled @Test void invokeMethodByMethodHandle() { - String key = "MethodHandle"; - String expected = "MethodHandle"; - RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); - applicationContext.publishEvent(redisCommandReplicatedEvent); + try (MockedStatic mock = mockStatic(RedisCommandsMethodHandles.class)) { + Method set = RedisStringCommands.class.getMethod("set", byte[].class, byte[].class); + mock.when(() -> transferMethodToMethodSignature(eq(set))) + .thenReturn("java.lang.Boolean set(byte[] key, byte[] value)"); + mock.when(() -> getMethodHandleBy(eq("java.lang.Boolean set(byte[] key, byte[] value)"))) + .thenCallRealMethod(); + String key = "MethodHandle"; + String expected = "MethodHandle"; + RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); + applicationContext.publishEvent(redisCommandReplicatedEvent); - String value = redisTemplate.opsForValue().get(key); - assertThat(value).isEqualTo(expected); + + String value = redisTemplate.opsForValue().get(key); + assertThat(value).isEqualTo(expected); + } catch (NoSuchMethodException e) { + + } } From 431b3ff7a54f1ecbdc594d9af9d3158edb2aa33c Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 20:35:32 +0800 Subject: [PATCH 22/32] feat: implement transferMethodToMethodSignature --- .../metadata/RedisCommandsMethodHandles.java | 72 +++++++++++++------ .../RedisCommandsMethodHandlesTest.java | 13 +++- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 8f9992a..549850d 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassType; +import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; @@ -38,13 +39,18 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; +import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.redisCommandMethodsCache; +import static java.util.stream.Collectors.toMap; + public class RedisCommandsMethodHandles { private static final Logger logger = LoggerFactory.getLogger(RedisCommandsMethodHandles.class); @@ -54,7 +60,9 @@ public class RedisCommandsMethodHandles { private static final ClassLoader CURRENT_CLASS_LOADER = RedisCommandsMethodHandles.class.getClassLoader(); private static final List> TARGET_CLASSES; + private static final Index index; private static final Map METHOD_HANDLE_MAP; + private static final Map METHOD_MAP; private RedisCommandsMethodHandles() { } @@ -75,6 +83,14 @@ public static MethodHandle getMethodHandleBy(String methodSignature) throws Meth return methodHandle; } + public static String transferMethodToMethodSignature(Method method) { + MethodInfo methodInfo = METHOD_MAP.get(method); + if (Objects.isNull(methodInfo)) { + throw new IllegalArgumentException(); + } + return methodInfo.toString(); + } + /** * get all public methods from {@link RedisCommands}
    * exclude {@link RedisCommands#execute}
    @@ -85,30 +101,27 @@ public static MethodHandle getMethodHandleBy(String methodSignature) throws Meth * @return List of RedisCommands all MethodInfo(include super interface) */ static List getAllRedisCommandMethods() { + Index index; try { - Index index = Index.of(TARGET_CLASSES); - - return index.getClassByName(RedisCommands.class) - .interfaceNames() - .stream() - .map(index::getClassByName) - .flatMap(classInfo -> classInfo.methods().stream()) - .filter(methodInfo -> Modifier.isPublic(methodInfo.flags())) - .collect(Collectors.toList()); + index = Index.of(TARGET_CLASSES); } catch (IOException e) { - logger.error("Can't get RedisCommands Methods", e); - throw new RuntimeException("Can't get RedisCommands Methods"); } + return index.getClassByName(RedisCommands.class) + .interfaceNames() + .stream() + .map(index::getClassByName) + .flatMap(classInfo -> classInfo.methods().stream()) + .filter(methodInfo -> Modifier.isPublic(methodInfo.flags())) + .collect(Collectors.toList()); } static Map initRedisCommandMethodHandle() { - return getAllRedisCommandMethods() .stream() .map(methodInfo -> new MethodRecord(methodInfo.toString(), findMethodHandle(methodInfo))) - .collect(Collectors.toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); + .collect(toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); } static MethodHandle findMethodHandle(MethodInfo methodInfo) { @@ -172,6 +185,26 @@ static Class getClassBy(Type type) { return ClassLoaderUtils.loadClass(type.name().toString(), CURRENT_CLASS_LOADER); } + static Map initRedisCommandMethodInfo() { + return redisCommandMethodsCache.values() + .stream() + .collect(toMap( + Function.identity(), + method -> index.getClassByName(method.getDeclaringClass()) + .method(method.getName(), getParameterTypes(method.getParameterTypes())) + )); + } + + private static Type[] getParameterTypes(Class[] parameterTypes) { + return Arrays.stream(parameterTypes) + .map(parameterType -> { + if (parameterType.isArray()) { + return Type.create(DotName.createSimple(parameterType), Type.Kind.ARRAY); + } else { + return Type.create(DotName.createSimple(parameterType), Type.Kind.CLASS); + } + }).toArray(Type[]::new); + } static { // NOTE: use List.of() to simplify the initial logic TARGET_CLASSES = new ArrayList<>(); @@ -191,14 +224,13 @@ static Class getClassBy(Type type) { TARGET_CLASSES.add(RedisGeoCommands.class); TARGET_CLASSES.add(RedisHyperLogLogCommands.class); + try { + index = Index.of(TARGET_CLASSES); + } catch (IOException e) { + throw new RuntimeException(e); + } METHOD_HANDLE_MAP = initRedisCommandMethodHandle(); - } - - public static String transferMethodToMethodSignature(Method method) { - // TODO 我写不出来了 - // example RedisStringCommands#set()的方法签名: - // target java.lang.Boolean set(byte[] key, byte[] value); - return ""; + METHOD_MAP = initRedisCommandMethodInfo(); } /** diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index ca1a3bd..bf02454 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -31,7 +31,9 @@ import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodHandle; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.initRedisCommandMethodInfo; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; +import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.redisCommandMethodsCache; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowableOfType; import static org.jboss.jandex.ArrayType.builder; @@ -93,7 +95,16 @@ void shouldThrowMethodHandleNotFoundExceptionWhenMiss() { } @Test - void shouldTransferMethodToMethodHandleSignature() throws NoSuchMethodException { + void shouldGetAllMethodInfoFromRedisCommandMethodsCache() { + Map map = initRedisCommandMethodInfo(); + assertThat(map) + .isNotNull() + .hasSize(redisCommandMethodsCache.size()); + + } + + @Test + void shouldTransferMethodToMethodHandleSignature() throws NoSuchMethodException, IOException { MethodInfo methodInfo = getMethodInfo(); Method setMethod = RedisStringCommands.class.getMethod("set", byte[].class, byte[].class); From d5a67e7519ab2fa4fcc9aadd46d11e887a03ca47 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 20:38:03 +0800 Subject: [PATCH 23/32] test: remove mock test RedisCommandReplicatedEvent by use MethodHandleEventHandle way --- ...HandleRedisCommandReplicatedEventTest.java | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index 3a0e787..686eb3c 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -88,30 +88,18 @@ class MethodHandleEventHandleTest { @Autowired RedisTemplate redisTemplate; - @Disabled + @Test void invokeMethodByMethodHandle() { - try (MockedStatic mock = mockStatic(RedisCommandsMethodHandles.class)) { - Method set = RedisStringCommands.class.getMethod("set", byte[].class, byte[].class); - mock.when(() -> transferMethodToMethodSignature(eq(set))) - .thenReturn("java.lang.Boolean set(byte[] key, byte[] value)"); - mock.when(() -> getMethodHandleBy(eq("java.lang.Boolean set(byte[] key, byte[] value)"))) - .thenCallRealMethod(); - - String key = "MethodHandle"; - String expected = "MethodHandle"; - RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); - applicationContext.publishEvent(redisCommandReplicatedEvent); - + String key = "MethodHandle"; + String expected = "MethodHandle"; + RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); + applicationContext.publishEvent(redisCommandReplicatedEvent); - String value = redisTemplate.opsForValue().get(key); - assertThat(value).isEqualTo(expected); - } catch (NoSuchMethodException e) { - } + String value = redisTemplate.opsForValue().get(key); + assertThat(value).isEqualTo(expected); } - - } From 757f24be05eb2552d907ebb9ff1886ef945cd499 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 21:11:34 +0800 Subject: [PATCH 24/32] refactor: refactor RedisCommandsMethodHandles --- ...dleRedisCommandReplicatedEventHandler.java | 7 +-- .../metadata/RedisCommandsMethodHandles.java | 48 ++++++++++++------- .../RedisCommandsMethodHandlesTest.java | 4 +- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java index e5f0fba..ebeea4c 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java @@ -1,23 +1,20 @@ package io.microsphere.redis.replicator.spring.event.handler; -import io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles; - import java.lang.reflect.Method; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; public class MethodHandleRedisCommandReplicatedEventHandler implements RedisCommandReplicatedEventHandler { @Override public void handleEvent(Method method, Object redisCommandObject, Object... args) throws Throwable { - String methodSignature = transferMethodToMethodSignature(method); int length = args.length; Object[] arguments = new Object[1 + args.length]; arguments[0] = redisCommandObject; for (int i = 0; i < length; i++) { arguments[i + 1] = args[i]; } - RedisCommandsMethodHandles.getMethodHandleBy(methodSignature).invokeWithArguments(arguments); + getMethodHandleBy(method).invokeWithArguments(arguments); } @Override diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java index 549850d..f5f31b5 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandles.java @@ -60,9 +60,10 @@ public class RedisCommandsMethodHandles { private static final ClassLoader CURRENT_CLASS_LOADER = RedisCommandsMethodHandles.class.getClassLoader(); private static final List> TARGET_CLASSES; - private static final Index index; + private static final Index REDIS_COMMANDS_INDEX; private static final Map METHOD_HANDLE_MAP; - private static final Map METHOD_MAP; + private static final Map METHOD_METHOD_INFO_MAP; + private static final Map METHOD_METHOD_HANDLE_MAP; private RedisCommandsMethodHandles() { } @@ -83,8 +84,23 @@ public static MethodHandle getMethodHandleBy(String methodSignature) throws Meth return methodHandle; } + public static MethodHandle getMethodHandleBy(Method method) throws MethodHandleNotFoundException { + MethodHandle methodHandle = METHOD_METHOD_HANDLE_MAP.get(method); + if (Objects.isNull(methodHandle)) { + logger.error("can't find MethodHandle from RedisCommands methodSignature:{}", method.getName()); + throw new MethodHandleNotFoundException("can't find MethodHandle from RedisCommands", method.toString()); + } + return methodHandle; + } + + /** + * find MethodInfo from METHOD_MAP + * + * @param method + * @return {@link MethodInfo#toString()} + */ public static String transferMethodToMethodSignature(Method method) { - MethodInfo methodInfo = METHOD_MAP.get(method); + MethodInfo methodInfo = METHOD_METHOD_INFO_MAP.get(method); if (Objects.isNull(methodInfo)) { throw new IllegalArgumentException(); } @@ -101,17 +117,10 @@ public static String transferMethodToMethodSignature(Method method) { * @return List of RedisCommands all MethodInfo(include super interface) */ static List getAllRedisCommandMethods() { - Index index; - try { - index = Index.of(TARGET_CLASSES); - } catch (IOException e) { - logger.error("Can't get RedisCommands Methods", e); - throw new RuntimeException("Can't get RedisCommands Methods"); - } - return index.getClassByName(RedisCommands.class) + return REDIS_COMMANDS_INDEX.getClassByName(RedisCommands.class) .interfaceNames() .stream() - .map(index::getClassByName) + .map(REDIS_COMMANDS_INDEX::getClassByName) .flatMap(classInfo -> classInfo.methods().stream()) .filter(methodInfo -> Modifier.isPublic(methodInfo.flags())) .collect(Collectors.toList()); @@ -120,11 +129,11 @@ static List getAllRedisCommandMethods() { static Map initRedisCommandMethodHandle() { return getAllRedisCommandMethods() .stream() - .map(methodInfo -> new MethodRecord(methodInfo.toString(), findMethodHandle(methodInfo))) + .map(methodInfo -> new MethodRecord(methodInfo.toString(), findMethodHandleBy(methodInfo))) .collect(toMap(MethodRecord::methodSignature, MethodRecord::methodHandle)); } - static MethodHandle findMethodHandle(MethodInfo methodInfo) { + static MethodHandle findMethodHandleBy(MethodInfo methodInfo) { Class klass = getClassBy(ClassType.create(methodInfo.declaringClass().name())); String methodName = methodInfo.name(); @@ -190,7 +199,7 @@ static Map initRedisCommandMethodInfo() { .stream() .collect(toMap( Function.identity(), - method -> index.getClassByName(method.getDeclaringClass()) + method -> REDIS_COMMANDS_INDEX.getClassByName(method.getDeclaringClass()) .method(method.getName(), getParameterTypes(method.getParameterTypes())) )); } @@ -205,6 +214,7 @@ private static Type[] getParameterTypes(Class[] parameterTypes) { } }).toArray(Type[]::new); } + static { // NOTE: use List.of() to simplify the initial logic TARGET_CLASSES = new ArrayList<>(); @@ -225,12 +235,16 @@ private static Type[] getParameterTypes(Class[] parameterTypes) { TARGET_CLASSES.add(RedisHyperLogLogCommands.class); try { - index = Index.of(TARGET_CLASSES); + REDIS_COMMANDS_INDEX = Index.of(TARGET_CLASSES); } catch (IOException e) { + logger.error("Index RedisCommands Error", e); throw new RuntimeException(e); } METHOD_HANDLE_MAP = initRedisCommandMethodHandle(); - METHOD_MAP = initRedisCommandMethodInfo(); + METHOD_METHOD_INFO_MAP = initRedisCommandMethodInfo(); + METHOD_METHOD_HANDLE_MAP = METHOD_METHOD_INFO_MAP.keySet() + .stream() + .collect(toMap(Function.identity(), key -> METHOD_HANDLE_MAP.get(METHOD_METHOD_INFO_MAP.get(key).toString()))); } /** diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index bf02454..2a293ff 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -26,7 +26,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.findMethodHandle; +import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.findMethodHandleBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getAllRedisCommandMethods; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getClassBy; import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; @@ -68,7 +68,7 @@ void shouldGetMethodHandleMapFromMethodInfo() { void shouldNewMethodHandleInstanceByMethodInfo() { MethodInfo methodInfo = getMethodInfo(); - MethodHandle methodHandle = findMethodHandle(methodInfo); + MethodHandle methodHandle = findMethodHandleBy(methodInfo); assertThat(methodHandle) .isNotNull(); } From 29762dc05518ed6afd3c198c87c9e0329d89c15b Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 21:13:32 +0800 Subject: [PATCH 25/32] refactor: reimport HandleRedisCommandReplicatedEventTest --- .../MethodHandleRedisCommandReplicatedEventHandler.java | 4 +--- .../spring/HandleRedisCommandReplicatedEventTest.java | 9 --------- .../spring/metadata/RedisCommandsMethodHandlesTest.java | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java index ebeea4c..7d250aa 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java @@ -11,9 +11,7 @@ public void handleEvent(Method method, Object redisCommandObject, Object... args int length = args.length; Object[] arguments = new Object[1 + args.length]; arguments[0] = redisCommandObject; - for (int i = 0; i < length; i++) { - arguments[i + 1] = args[i]; - } + System.arraycopy(args, 0, arguments, 1, length); getMethodHandleBy(method).invokeWithArguments(arguments); } diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index 686eb3c..aa687b8 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -2,15 +2,11 @@ import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; import io.microsphere.redis.spring.event.RedisCommandEvent; -import io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.ApplicationContext; -import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -20,16 +16,11 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.io.IOException; import java.lang.reflect.Method; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.transferMethodToMethodSignature; import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.findWriteCommandMethod; import static io.microsphere.redis.spring.serializer.RedisCommandEventSerializer.VERSION_V1; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mockStatic; import static org.testcontainers.utility.DockerImageName.parse; @Testcontainers diff --git a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java index 2a293ff..dad8590 100644 --- a/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java +++ b/microsphere-redis-spring/src/test/java/io/microsphere/redis/spring/metadata/RedisCommandsMethodHandlesTest.java @@ -104,7 +104,7 @@ void shouldGetAllMethodInfoFromRedisCommandMethodsCache() { } @Test - void shouldTransferMethodToMethodHandleSignature() throws NoSuchMethodException, IOException { + void shouldTransferMethodToMethodHandleSignature() throws NoSuchMethodException { MethodInfo methodInfo = getMethodInfo(); Method setMethod = RedisStringCommands.class.getMethod("set", byte[].class, byte[].class); From c08af076a4f2daa233cdfc750594167bab8daf88 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 21:21:20 +0800 Subject: [PATCH 26/32] build: jandex jandex 3.1.6 -> 3.1.7 --- microsphere-redis-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-redis-parent/pom.xml b/microsphere-redis-parent/pom.xml index 350ea65..3e3227f 100644 --- a/microsphere-redis-parent/pom.xml +++ b/microsphere-redis-parent/pom.xml @@ -28,7 +28,7 @@ 3.1.6 2021.0.5.0 2021.0.7 - 3.1.6 + 3.1.7 1.19.5 From 34f05b47e1ca422b21695334dc3b4893dd5b02ef Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Sun, 24 Mar 2024 21:22:17 +0800 Subject: [PATCH 27/32] test: remove unnecessary --- .../spring/HandleRedisCommandReplicatedEventTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index aa687b8..4a06f10 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -30,8 +30,6 @@ class HandleRedisCommandReplicatedEventTest { @DynamicPropertySource static void redisProperties(DynamicPropertyRegistry registry) { - - System.out.println(registry); registry.add("spring.redis.host", () -> redisContainer.getHost()); registry.add("spring.redis.port", () -> redisContainer.getMappedPort(6379)); } From 3719ab496b5be72256401fa23248da571190644e Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Mon, 25 Mar 2024 09:46:30 +0800 Subject: [PATCH 28/32] refactor: writeCommandMethodHandlesCache add writeCommandMethodHandlesCache --- ...dleRedisCommandReplicatedEventHandler.java | 4 +-- .../metadata/RedisMetadataRepository.java | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java index 7d250aa..fd3b2fe 100644 --- a/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java +++ b/microsphere-redis-replicator-spring/src/main/java/io/microsphere/redis/replicator/spring/event/handler/MethodHandleRedisCommandReplicatedEventHandler.java @@ -2,7 +2,7 @@ import java.lang.reflect.Method; -import static io.microsphere.redis.spring.metadata.RedisCommandsMethodHandles.getMethodHandleBy; +import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.getWriteCommandMethodHandle; public class MethodHandleRedisCommandReplicatedEventHandler implements RedisCommandReplicatedEventHandler { @@ -12,7 +12,7 @@ public void handleEvent(Method method, Object redisCommandObject, Object... args Object[] arguments = new Object[1 + args.length]; arguments[0] = redisCommandObject; System.arraycopy(args, 0, arguments, 1, length); - getMethodHandleBy(method).invokeWithArguments(arguments); + getWriteCommandMethodHandle(method).invokeWithArguments(arguments); } @Override diff --git a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisMetadataRepository.java b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisMetadataRepository.java index 8580ec5..c3e4971 100644 --- a/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisMetadataRepository.java +++ b/microsphere-redis-spring/src/main/java/io/microsphere/redis/spring/metadata/RedisMetadataRepository.java @@ -13,6 +13,9 @@ import org.yaml.snakeyaml.Yaml; import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.Arrays; @@ -67,6 +70,12 @@ public class RedisMetadataRepository { */ static final Map writeCommandMethodsCache = new HashMap<>(256); + private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup(); + /** + * Method Simple signature with {@link MethodHandle} object caching (reduces reflection cost) + */ + static final Map writeCommandMethodHandlesCache = new HashMap<>(256); + /** * MethodMetadata cache *
      @@ -227,6 +236,11 @@ public static Set getWriteCommandMethods() { return writeCommandMethodsMetadata.keySet(); } + public static MethodHandle getWriteCommandMethodHandle(Method method) { + String id = buildCommandMethodId(method); + return writeCommandMethodHandlesCache.get(id); + } + /** * Gets the {@link RedisCommands} command interface for the specified Class name {@link Class} * @@ -270,6 +284,7 @@ private static void initWriteCommandMethod(Method method) { } if (initWriteCommandMethodParameterMetadata(method, parameterTypes)) { initWriteCommandMethodCache(method, parameterTypes); + initWriteCommandMethodHandleCache(method); } } catch (Throwable e) { logger.error("Unable to initialize write command method[{}], Reason: {}", method, e.getMessage()); @@ -300,4 +315,25 @@ private static void initWriteCommandMethodCache(Method method, Class[] parame } } + + private static void initWriteCommandMethodHandleCache(Method method) { + String id = buildCommandMethodId(method); + + MethodHandle methodHandle; + try { + methodHandle = PUBLIC_LOOKUP.findVirtual( + method.getDeclaringClass(), + method.getName(), + MethodType.methodType(method.getReturnType(), method.getParameterTypes())); + } catch (NoSuchMethodException | IllegalAccessException e) { + logger.error("The Redis Write Command MethodHandle[{}] can't find", id); + return; + } + + if (writeCommandMethodHandlesCache.putIfAbsent(id, methodHandle) == null) { + logger.debug("Caches the Redis Write Command MethodHandle : {}", id); + } else { + logger.warn("The Redis Write Command MethodHandle[{}] was cached", id); + } + } } From 9da4aef81bd454f4f70fb4c044ed61c316e2c776 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Mon, 25 Mar 2024 20:55:15 +0800 Subject: [PATCH 29/32] test: test RedisCommandReplicatedEventHandler add judge eventHandler --- .../spring/HandleRedisCommandReplicatedEventTest.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java index 4a06f10..33a1b3d 100644 --- a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/replicator/spring/HandleRedisCommandReplicatedEventTest.java @@ -1,6 +1,7 @@ package io.microsphere.redis.replicator.spring; import io.microsphere.redis.replicator.spring.event.RedisCommandReplicatedEvent; +import io.microsphere.redis.replicator.spring.event.handler.RedisCommandReplicatedEventHandler; import io.microsphere.redis.spring.event.RedisCommandEvent; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -49,6 +50,9 @@ class ReflectEventHandleTest { @Autowired RedisTemplate redisTemplate; + @Autowired + RedisCommandReplicator redisCommandReplicator; + @Test void invokeMethodByReflect() { String key = "Reflect"; @@ -56,6 +60,8 @@ void invokeMethodByReflect() { RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); applicationContext.publishEvent(redisCommandReplicatedEvent); + assertThat(redisCommandReplicator.eventHandler.name()) + .isEqualTo(RedisCommandReplicatedEventHandler.EventHandleName.REFLECT.name()); String value = redisTemplate.opsForValue().get(key); assertThat(value).isEqualTo(expected); } @@ -77,6 +83,8 @@ class MethodHandleEventHandleTest { @Autowired RedisTemplate redisTemplate; + @Autowired + RedisCommandReplicator redisCommandReplicator; @Test void invokeMethodByMethodHandle() { @@ -85,7 +93,8 @@ void invokeMethodByMethodHandle() { RedisCommandReplicatedEvent redisCommandReplicatedEvent = getRedisCommandReplicatedEvent(key, expected); applicationContext.publishEvent(redisCommandReplicatedEvent); - + assertThat(redisCommandReplicator.eventHandler.name()) + .isEqualTo(RedisCommandReplicatedEventHandler.EventHandleName.METHOD_HANDLE.name()); String value = redisTemplate.opsForValue().get(key); assertThat(value).isEqualTo(expected); } From d99bdc92c254e0af3b5065566ea37cf8a795de84 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Fri, 29 Mar 2024 10:47:49 +0800 Subject: [PATCH 30/32] build: remove unnecessary --- microsphere-redis-replicator-spring/pom.xml | 6 +----- microsphere-redis-spring/pom.xml | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/microsphere-redis-replicator-spring/pom.xml b/microsphere-redis-replicator-spring/pom.xml index f989028..e2dc753 100644 --- a/microsphere-redis-replicator-spring/pom.xml +++ b/microsphere-redis-replicator-spring/pom.xml @@ -98,16 +98,12 @@ test - - org.testcontainers - testcontainers - test - org.testcontainers junit-jupiter test + org.mockito mockito-inline diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index 6150489..b5a75e1 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -107,9 +107,11 @@ mockito-inline test + org.junit.jupiter junit-jupiter + test \ No newline at end of file From 97fcf62f26fa4b3c626a8a208081116871a0fe1c Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Wed, 26 Nov 2025 20:47:17 +0800 Subject: [PATCH 31/32] build: build project --- microsphere-redis-parent/pom.xml | 2 +- microsphere-redis-replicator-spring/pom.xml | 4 ++-- microsphere-redis-spring/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-redis-parent/pom.xml b/microsphere-redis-parent/pom.xml index f1015e1..e315a00 100644 --- a/microsphere-redis-parent/pom.xml +++ b/microsphere-redis-parent/pom.xml @@ -19,7 +19,7 @@ Microsphere Redis Parent - ${revision} + 0.0.1 3.1.7 1.19.5 diff --git a/microsphere-redis-replicator-spring/pom.xml b/microsphere-redis-replicator-spring/pom.xml index e2dc753..c29768e 100644 --- a/microsphere-redis-replicator-spring/pom.xml +++ b/microsphere-redis-replicator-spring/pom.xml @@ -28,7 +28,7 @@ io.github.microsphere-projects microsphere-spring-context - ${revision} + 0.0.1 @@ -76,7 +76,7 @@ io.github.microsphere-projects microsphere-spring-test - ${revision} + 0.0.1 test diff --git a/microsphere-redis-spring/pom.xml b/microsphere-redis-spring/pom.xml index b5a75e1..e3de934 100644 --- a/microsphere-redis-spring/pom.xml +++ b/microsphere-redis-spring/pom.xml @@ -86,7 +86,7 @@ io.github.microsphere-projects microsphere-spring-test - ${revision} + 0.0.1 test From 16074b25471c8b44400dd6d94ef29e6cb8bfc686 Mon Sep 17 00:00:00 2001 From: "li.qi" <15868175516@163.com> Date: Wed, 26 Nov 2025 21:43:46 +0800 Subject: [PATCH 32/32] test: benchmark RedisCommandReplicatedEventHandler --- microsphere-redis-parent/pom.xml | 11 +++ microsphere-redis-replicator-spring/pom.xml | 36 +++++++- ...ommandReplicatedEventHandlerBenchmark.java | 90 +++++++++++++++++++ .../benchmark/runner/BenchmarkRunner.java | 20 +++++ 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/RedisCommandReplicatedEventHandlerBenchmark.java create mode 100644 microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/runner/BenchmarkRunner.java diff --git a/microsphere-redis-parent/pom.xml b/microsphere-redis-parent/pom.xml index e315a00..9a61329 100644 --- a/microsphere-redis-parent/pom.xml +++ b/microsphere-redis-parent/pom.xml @@ -22,6 +22,7 @@ 0.0.1 3.1.7 1.19.5 + 1.37 @@ -51,6 +52,16 @@ pom import + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + diff --git a/microsphere-redis-replicator-spring/pom.xml b/microsphere-redis-replicator-spring/pom.xml index c29768e..57d81ae 100644 --- a/microsphere-redis-replicator-spring/pom.xml +++ b/microsphere-redis-replicator-spring/pom.xml @@ -109,6 +109,40 @@ mockito-inline test - + + org.openjdk.jmh + jmh-core + + + org.openjdk.jmh + jmh-generator-annprocess + provided + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + benchmarks + + + org.openjdk.jmh.Main + + + + + + + + \ No newline at end of file diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/RedisCommandReplicatedEventHandlerBenchmark.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/RedisCommandReplicatedEventHandlerBenchmark.java new file mode 100644 index 0000000..8ffd432 --- /dev/null +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/RedisCommandReplicatedEventHandlerBenchmark.java @@ -0,0 +1,90 @@ +package io.microsphere.redis.benchmark; + +import io.microsphere.redis.replicator.spring.event.handler.MethodHandleRedisCommandReplicatedEventHandler; +import io.microsphere.redis.replicator.spring.event.handler.RedisCommandReplicatedEventHandler; +import io.microsphere.redis.replicator.spring.event.handler.ReflectRedisCommandReplicatedEventHandler; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.testcontainers.containers.GenericContainer; + +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import static io.microsphere.redis.spring.metadata.RedisMetadataRepository.findWriteCommandMethod; +import static org.testcontainers.utility.DockerImageName.parse; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 20, time = 1) +@Fork(3) +public class RedisCommandReplicatedEventHandlerBenchmark { + static GenericContainer redisContainer; + RedisStringCommands redisStringCommands; + Method method; + RedisCommandReplicatedEventHandler methodHandleHandler; + RedisCommandReplicatedEventHandler reflectHandler; + + byte[] key; + byte[] value; + + @Setup(Level.Trial) + public void setup() throws Exception { + // 启动 PostgreSQL 容器 + redisContainer = new GenericContainer<>(parse("redis:latest")).withExposedPorts(6379); + redisContainer.start(); + + String interfaceName = "org.springframework.data.redis.connection.RedisStringCommands"; + String methodName = "set"; + String[] parameterTypes = new String[]{"[B", "[B"}; + method = findWriteCommandMethod(interfaceName, methodName, parameterTypes); + + LettuceConnectionFactory redisConnectionFactory = new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisContainer.getHost(), redisContainer.getFirstMappedPort())); + redisConnectionFactory.afterPropertiesSet(); + RedisConnection connection = redisConnectionFactory.getConnection(); + redisStringCommands = connection.stringCommands(); + + methodHandleHandler = new MethodHandleRedisCommandReplicatedEventHandler(); + reflectHandler = new ReflectRedisCommandReplicatedEventHandler(); + key = "key".getBytes(StandardCharsets.UTF_8); + value = "value".getBytes(StandardCharsets.UTF_8); + } + + @Benchmark + public void benchmarkDirect() throws Throwable { + redisStringCommands.set(key, value); + } + + @Benchmark + public void benchmarkMethodHandleRedisCommandReplicatedEventHandler() throws Throwable { + methodHandleHandler.handleEvent(method, redisStringCommands, key, value); + } + + @Benchmark + public void benchmarkReflectRedisCommandReplicatedEventHandler() throws Throwable { + reflectHandler.handleEvent(method, redisStringCommands, key, value); + } + + @TearDown(Level.Trial) + public void tearDown() throws Exception { + if (redisContainer != null) { + redisContainer.stop(); + } + } +} diff --git a/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/runner/BenchmarkRunner.java b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/runner/BenchmarkRunner.java new file mode 100644 index 0000000..d018d22 --- /dev/null +++ b/microsphere-redis-replicator-spring/src/test/java/io/microsphere/redis/benchmark/runner/BenchmarkRunner.java @@ -0,0 +1,20 @@ +package io.microsphere.redis.benchmark.runner; + +import io.microsphere.redis.benchmark.RedisCommandReplicatedEventHandlerBenchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class BenchmarkRunner { + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(RedisCommandReplicatedEventHandlerBenchmark.class.getSimpleName()) + .warmupIterations(3) + .measurementIterations(5) + .forks(1) + .build(); + + new Runner(options).run(); + } +}