From 1e4f48f293ba10fdcb79cc5a32a0e2e8fcf26e39 Mon Sep 17 00:00:00 2001 From: luckzhu Date: Tue, 20 Oct 2020 17:27:04 +0800 Subject: [PATCH 1/2] annotation-based-cache-decorator --- .../com/github/hcsp/annotation/Cache.java | 4 + .../hcsp/annotation/CacheClassDecorator.java | 99 ++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) 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..cb4312c 100644 --- a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java +++ b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java @@ -1,5 +1,20 @@ package com.github.hcsp.annotation; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +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.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 +22,90 @@ 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(); + } + + public static class CacheAdvisor { + private static ConcurrentHashMap cacheMap = 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(method.getName(), thisObject, arguments); + final CacheValue cacheValue = cacheMap.get(cacheKey); + if (cacheValue != null) { + if (cacheExpires(cacheValue, method)) { + return getRealResultAndPutIntoCache(superCall, cacheKey); + } else { + return cacheValue.value; + } + } else { + return getRealResultAndPutIntoCache(superCall, cacheKey); + } + } + + private static Object getRealResultAndPutIntoCache(@SuperCall Callable superCall, CacheKey cacheKey) throws Exception { + Object realMethodInvokedResult = superCall.call(); + cacheMap.put(cacheKey, new CacheValue(realMethodInvokedResult, System.currentTimeMillis())); + return realMethodInvokedResult; + } + } + + private static boolean cacheExpires(CacheValue cacheValue, Method method) { + long time = cacheValue.time; + int cacheSeconds = method.getAnnotation(Cache.class).cacheSeconds(); + return System.currentTimeMillis() - time > cacheSeconds * 1000; + } + + private static class CacheKey { + private String methodName; + private Object thisObject; + private Object[] arguments; + + public CacheKey(String methodName, Object thisObject, Object[] arguments) { + this.methodName = methodName; + this.thisObject = thisObject; + 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(methodName, cacheKey.methodName) && + Objects.equals(thisObject, cacheKey.thisObject) && + Arrays.equals(arguments, cacheKey.arguments); + } + + @Override + public int hashCode() { + int result = Objects.hash(methodName, thisObject); + result = 31 * result + Arrays.hashCode(arguments); + return result; + } + } + + private static class CacheValue { + private Object value; + private long time; + + public CacheValue(Object value, long time) { + this.value = value; + this.time = time; + } } public static void main(String[] args) throws Exception { From 2f8a674763b07afa568f5001d1d22843a4aab6ad Mon Sep 17 00:00:00 2001 From: luckzhu Date: Tue, 20 Oct 2020 17:33:03 +0800 Subject: [PATCH 2/2] fixed code style --- .../hcsp/annotation/CacheClassDecorator.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java index cb4312c..9ca1152 100644 --- a/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java +++ b/src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java @@ -74,7 +74,7 @@ private static class CacheKey { private Object thisObject; private Object[] arguments; - public CacheKey(String methodName, Object thisObject, Object[] arguments) { + CacheKey(String methodName, Object thisObject, Object[] arguments) { this.methodName = methodName; this.thisObject = thisObject; this.arguments = arguments; @@ -82,12 +82,17 @@ public CacheKey(String methodName, Object thisObject, Object[] arguments) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } CacheKey cacheKey = (CacheKey) o; - return Objects.equals(methodName, cacheKey.methodName) && - Objects.equals(thisObject, cacheKey.thisObject) && - Arrays.equals(arguments, cacheKey.arguments); + return Objects.equals(methodName, cacheKey.methodName) + && Objects.equals(thisObject, cacheKey.thisObject) + && Arrays.equals(arguments, cacheKey.arguments); } @Override @@ -102,7 +107,7 @@ private static class CacheValue { private Object value; private long time; - public CacheValue(Object value, long time) { + CacheValue(Object value, long time) { this.value = value; this.time = time; }