From d3f2dd43f58398dfb130d4c446cbdde711a370e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cidpeng=E2=80=9D?= <630606938@qq.com> Date: Sat, 23 Oct 2021 23:17:06 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E7=BC=93=E5=AD=98=E8=A3=85=E9=A5=B0=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 + .../com/github/hcsp/annotation/Cache.java | 4 + .../hcsp/annotation/CacheClassDecorator.java | 101 +++++++++++++++++- .../github/hcsp/annotation/DataService.java | 2 +- 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cf436d9..381e3c3 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,11 @@ 5.6.0 test + + net.bytebuddy + byte-buddy + 1.11.21 + diff --git a/src/main/java/com/github/hcsp/annotation/Cache.java b/src/main/java/com/github/hcsp/annotation/Cache.java index b8651b5..63febca 100644 --- a/src/main/java/com/github/hcsp/annotation/Cache.java +++ b/src/main/java/com/github/hcsp/annotation/Cache.java @@ -1,5 +1,9 @@ package com.github.hcsp.annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) public @interface Cache { // 标记缓存的时长(秒),默认60s int cacheSeconds() default 60; diff --git a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java index fb5d531..8f666f6 100644 --- a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java +++ b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java @@ -1,5 +1,16 @@ package com.github.hcsp.annotation; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.*; +import net.bytebuddy.matcher.ElementMatchers; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; + public class CacheClassDecorator { // 将传入的服务类Class进行增强 // 使得返回一个具有如下功能的Class: @@ -7,8 +18,94 @@ public class CacheClassDecorator { // 这意味着,在短时间内调用同一个服务的同一个@Cache方法两次 // 它实际上只被调用一次,第二次的结果直接从缓存中获取 // 注意,缓存的实现需要是线程安全的 + @SuppressWarnings("unchecked") public static Class decorate(Class klass) { - return klass; + return (Class) new ByteBuddy() + .subclass(klass) + .method(ElementMatchers.isAnnotatedWith(Cache.class)) + .intercept(MethodDelegation.to(CacheAdvisor.class)) + .make() + .load(klass.getClassLoader()) + .getLoaded(); + } + + private static class CacheKey { + private final Object thisObject; + private final String methodName; + private final Object[] arguments; + + private CacheKey(Object thisObject, String methodName, Object[] arguments) { + this.thisObject = thisObject; + this.methodName = methodName; + this.arguments = arguments; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CacheKey cacheKey = (CacheKey) o; + return Objects.equals(thisObject, cacheKey.thisObject) && Objects.equals(methodName, cacheKey.methodName) && Arrays.equals(arguments, cacheKey.arguments); + } + + @Override + public int hashCode() { + int result = Objects.hash(thisObject, methodName); + result = 31 * result + Arrays.hashCode(arguments); + return result; + } + } + + private static class CacheValue { + private final Object value; + private final long time; + + private CacheValue(Object value, long time) { + this.value = value; + this.time = time; + } + } + + public static class CacheAdvisor { + private static final ConcurrentHashMap cache = new ConcurrentHashMap<>(); + + @RuntimeType + public static Object cache( + @SuperCall Callable superCall, + @Origin Method method, + @This Object thisObject, + @AllArguments Object[] arguments) throws Exception { + CacheKey cacheKey = new CacheKey(thisObject, method.getName(), arguments); + final CacheValue resultExistingInCache = cache.get(cacheKey); + + if (resultExistingInCache != null) { + + if (cacheExpires(resultExistingInCache, method)) { + return invokeRealMethodAndPutIntoCache(superCall, cacheKey); + } else { + return resultExistingInCache.value; + } + } else { + return invokeRealMethodAndPutIntoCache(superCall, cacheKey); + } + } + + private static Object invokeRealMethodAndPutIntoCache(@SuperCall Callable superCall, CacheKey cacheKey) throws Exception { + Object realMethodInvocationResult = superCall.call(); + cache.put(cacheKey, new CacheValue(realMethodInvocationResult, System.currentTimeMillis())); + return realMethodInvocationResult; + } + + private static boolean cacheExpires(CacheValue cacheValue, Method method) { + long time = cacheValue.time; + int cacheSeconds = method.getAnnotation(Cache.class).cacheSeconds(); + + return System.currentTimeMillis() - time > cacheSeconds * 1000L; + } } public static void main(String[] args) throws Exception { @@ -18,6 +115,8 @@ public static void main(String[] args) throws Exception { System.out.println(dataService.queryData(1)); Thread.sleep(1 * 1000); System.out.println(dataService.queryData(1)); + Thread.sleep(3 * 1000); + System.out.println(dataService.queryData(1)); // 无缓存的查询:两次都执行了真正的查询操作 System.out.println(dataService.queryDataWithoutCache(1)); diff --git a/src/main/java/com/github/hcsp/annotation/DataService.java b/src/main/java/com/github/hcsp/annotation/DataService.java index fefb56c..d01da0d 100644 --- a/src/main/java/com/github/hcsp/annotation/DataService.java +++ b/src/main/java/com/github/hcsp/annotation/DataService.java @@ -12,7 +12,7 @@ public class DataService { * @param id 数据ID * @return 查询到的数据列表 */ - @Cache + @Cache(cacheSeconds = 2) public List queryData(int id) { // 模拟一个查询操作 Random random = new Random(); From 8660662bee5fb4eeddc2c0d9299d336d4b24d25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cidpeng=E2=80=9D?= <630606938@qq.com> Date: Sat, 23 Oct 2021 23:28:03 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E7=BC=93=E5=AD=98=E8=A3=85=E9=A5=B0=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/hcsp/annotation/CacheClassDecorator.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java index 8f666f6..456fe9f 100644 --- a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java +++ b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java @@ -2,7 +2,11 @@ import net.bytebuddy.ByteBuddy; import net.bytebuddy.implementation.MethodDelegation; -import net.bytebuddy.implementation.bind.annotation.*; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.implementation.bind.annotation.AllArguments; import net.bytebuddy.matcher.ElementMatchers; import java.lang.reflect.Method; @@ -71,7 +75,7 @@ private CacheValue(Object value, long time) { } public static class CacheAdvisor { - private static final ConcurrentHashMap cache = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap aache = new ConcurrentHashMap<>(); @RuntimeType public static Object cache( @@ -80,7 +84,7 @@ public static Object cache( @This Object thisObject, @AllArguments Object[] arguments) throws Exception { CacheKey cacheKey = new CacheKey(thisObject, method.getName(), arguments); - final CacheValue resultExistingInCache = cache.get(cacheKey); + final CacheValue resultExistingInCache = aache.get(cacheKey); if (resultExistingInCache != null) { @@ -96,7 +100,7 @@ public static Object cache( private static Object invokeRealMethodAndPutIntoCache(@SuperCall Callable superCall, CacheKey cacheKey) throws Exception { Object realMethodInvocationResult = superCall.call(); - cache.put(cacheKey, new CacheValue(realMethodInvocationResult, System.currentTimeMillis())); + aache.put(cacheKey, new CacheValue(realMethodInvocationResult, System.currentTimeMillis())); return realMethodInvocationResult; } From 9aa67df13ba2a01934f122887b9451200d17edc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cidpeng=E2=80=9D?= <630606938@qq.com> Date: Sat, 23 Oct 2021 23:34:18 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E7=BC=93=E5=AD=98=E8=A3=85=E9=A5=B0=E5=99=A82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/github/hcsp/annotation/CacheClassDecorator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java index 456fe9f..3f09b25 100644 --- a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java +++ b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java @@ -75,7 +75,7 @@ private CacheValue(Object value, long time) { } public static class CacheAdvisor { - private static final ConcurrentHashMap aache = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap cacheMap = new ConcurrentHashMap<>(); @RuntimeType public static Object cache( @@ -84,7 +84,7 @@ public static Object cache( @This Object thisObject, @AllArguments Object[] arguments) throws Exception { CacheKey cacheKey = new CacheKey(thisObject, method.getName(), arguments); - final CacheValue resultExistingInCache = aache.get(cacheKey); + final CacheValue resultExistingInCache = cacheMap.get(cacheKey); if (resultExistingInCache != null) { @@ -100,7 +100,7 @@ public static Object cache( private static Object invokeRealMethodAndPutIntoCache(@SuperCall Callable superCall, CacheKey cacheKey) throws Exception { Object realMethodInvocationResult = superCall.call(); - aache.put(cacheKey, new CacheValue(realMethodInvocationResult, System.currentTimeMillis())); + cacheMap.put(cacheKey, new CacheValue(realMethodInvocationResult, System.currentTimeMillis())); return realMethodInvocationResult; }