From c94be39f025d66f462c586279be072b7113b2fb6 Mon Sep 17 00:00:00 2001 From: redkale Date: Thu, 30 May 2024 15:21:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0CacheKeyGenerator=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/redkale/cache/Cached.java | 15 +++-- .../org/redkale/cache/spi/CacheAction.java | 67 +++++++++++++------ .../cache/spi/CacheAsmMethodBoost.java | 35 ++++------ .../org/redkale/cache/spi/CacheEntry.java | 24 ------- .../redkale/cache/spi/CacheKeyGenerator.java | 39 +++++++++++ .../redkale/cache/spi/CacheModuleEngine.java | 52 ++++++++++++++ 6 files changed, 159 insertions(+), 73 deletions(-) create mode 100644 src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java diff --git a/src/main/java/org/redkale/cache/Cached.java b/src/main/java/org/redkale/cache/Cached.java index ab587a0c3..b799a6661 100644 --- a/src/main/java/org/redkale/cache/Cached.java +++ b/src/main/java/org/redkale/cache/Cached.java @@ -3,18 +3,20 @@ */ package org.redkale.cache; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.METHOD; import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit; import org.redkale.service.LoadMode; /** * 标记在Service的缓存接口, 方法有以下限制:
- * 1、方法返回类型不能是void/CompletableFuture<Void> 2、方法返回类型必须可json序列化 3、方法必须是protected/public 4、方法不能是final/static + * 1、方法返回类型不能是void/CompletableFuture<Void>
+ * 2、方法返回类型必须可json序列化
+ * 3、方法必须是protected/public
+ * 4、方法不能是final/static
* * @since 2.8.0 */ @@ -24,7 +26,10 @@ import org.redkale.service.LoadMode; public @interface Cached { /** - * 缓存的key,支持参数动态组合,比如"key_#{id}" + * 缓存的key,支持参数动态组合,比如"key_#{id}"
+ * '@'开头的key值视为CacheKeyGenerator对象名称
+ * + * @see org.redkale.cache.spi.CacheKeyGenerator#name() * * @return 键 */ diff --git a/src/main/java/org/redkale/cache/spi/CacheAction.java b/src/main/java/org/redkale/cache/spi/CacheAction.java index f936d5db5..e85991ec5 100644 --- a/src/main/java/org/redkale/cache/spi/CacheAction.java +++ b/src/main/java/org/redkale/cache/spi/CacheAction.java @@ -3,6 +3,7 @@ */ package org.redkale.cache.spi; +import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.time.Duration; @@ -13,6 +14,7 @@ import org.redkale.annotation.Nullable; import org.redkale.annotation.Resource; import org.redkale.cache.CacheManager; import org.redkale.convert.json.JsonConvert; +import org.redkale.inject.ResourceFactory; import org.redkale.util.Environment; import org.redkale.util.MultiHashKey; import org.redkale.util.ThrowSupplier; @@ -37,6 +39,8 @@ public class CacheAction { private final CacheEntry cached; + private final Method method; + // Supplier对象的类型 private final Type resultType; @@ -55,10 +59,6 @@ public class CacheAction { // 获取动态的字段名 private final String fieldName; - // 方法参数类型 - @Nullable - private final Class[] paramTypes; - // 方法参数名 @Nullable private final String[] paramNames; @@ -69,8 +69,11 @@ public class CacheAction { // 模板key String templetKey; - // 缓存的key - private MultiHashKey dynKey; + // 缓存key生成器 + private CacheKeyGenerator keyGenerator; + + // 父对象 + private Object service; // 本地缓存过期时长,Duration.ZERO为永不过期,为null表示不本地缓存 private Duration localExpire; @@ -78,34 +81,33 @@ public class CacheAction { // 远程缓存过期时长,Duration.ZERO为永不过期,为null表示不远程缓存 private Duration remoteExpire; - CacheAction( - CacheEntry cached, - Type returnType, - Class serviceClass, - Class[] paramTypes, - String[] paramNames, - String methodName, - String fieldName) { + CacheAction(CacheEntry cached, Method method, Class serviceClass, String[] paramNames, String fieldName) { this.cached = cached; + this.method = method; this.nullable = cached.isNullable(); this.serviceClass = Objects.requireNonNull(serviceClass); - this.paramTypes = paramTypes; this.paramNames = paramNames; - this.methodName = Objects.requireNonNull(methodName); + this.methodName = method.getName(); this.fieldName = Objects.requireNonNull(fieldName); this.templetKey = cached.getKey(); + Type returnType = method.getGenericReturnType(); this.async = CompletableFuture.class.isAssignableFrom(TypeToken.typeToClass(returnType)); this.resultType = this.async ? ((ParameterizedType) returnType).getActualTypeArguments()[0] : returnType; } - void init() { + void init(ResourceFactory resourceFactory, Object service) { this.hash = cached.getHash().trim().isEmpty() || CacheManager.DEFAULT_HASH.equals(cached.getHash()) ? CacheManager.DEFAULT_HASH : environment.getPropertyValue(cached.getHash()); String key = environment.getPropertyValue(cached.getKey()); this.templetKey = key; - this.dynKey = MultiHashKey.create(paramNames, key); - this.localExpire = createDuration(cached.getLocalExpire()); + if (key.startsWith("@")) { // 动态加载缓存key生成器 + String generatorName = key.substring(1); + this.keyGenerator = resourceFactory.findChild(generatorName, CacheKeyGenerator.class); + } else { + MultiHashKey dynKey = MultiHashKey.create(paramNames, key); + this.keyGenerator = (t, a, args) -> dynKey.keyFor(args); + } this.remoteExpire = createDuration(cached.getRemoteExpire()); } @@ -114,10 +116,22 @@ public class CacheAction { if (async) { ThrowSupplier supplier0 = supplier; return (T) manager.bothGetSetAsync( - hash, dynKey.keyFor(args), resultType, nullable, localExpire, remoteExpire, supplier0); + hash, + keyGenerator.generate(service, this, args), + resultType, + nullable, + localExpire, + remoteExpire, + supplier0); } else { return manager.bothGetSet( - hash, dynKey.keyFor(args), resultType, nullable, localExpire, remoteExpire, supplier); + hash, + keyGenerator.generate(service, this, args), + resultType, + nullable, + localExpire, + remoteExpire, + supplier); } } @@ -146,14 +160,23 @@ public class CacheAction { } } + public CacheEntry getCached() { + return cached; + } + + public Method getMethod() { + return method; + } + @Override public String toString() { return "{" + "\"serviceClass\":" + serviceClass.getName() + ",\"methodName\":\"" + methodName + "\"" + ",\"fieldName\":\"" + fieldName + "\"" - + ",\"paramTypes\":" + JsonConvert.root().convertTo(paramTypes) + + ",\"paramTypes\":" + JsonConvert.root().convertTo(method.getParameterTypes()) + ",\"paramNames\":" + JsonConvert.root().convertTo(paramNames) + + ",\"templetKey\":\"" + templetKey + "\"" + ",\"resultType\":\"" + resultType + "\"" + ",\"cache\":" + cached + "}"; diff --git a/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java index 55613647a..4f0ce248b 100644 --- a/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java +++ b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java @@ -169,13 +169,7 @@ public class CacheAsmMethodBoost extends AsmMethodBoost { mv.visitMaxs(20, 20); mv.visitEnd(); CacheAction action = new CacheAction( - new CacheEntry(cached), - method.getGenericReturnType(), - serviceType, - method.getParameterTypes(), - methodBean.fieldNameArray(), - method.getName(), - dynFieldName); + new CacheEntry(cached), method, serviceType, methodBean.fieldNameArray(), dynFieldName); actions.put(dynFieldName, action); } { // ThrowSupplier @@ -223,27 +217,24 @@ public class CacheAsmMethodBoost extends AsmMethodBoost { String dynFieldName = cached.dynField(); AsmMethodBean methodBean = AsmMethodBean.get(methodBeans, method); CacheAction action = new CacheAction( - new CacheEntry(cached), - method.getGenericReturnType(), - serviceType, - method.getParameterTypes(), - methodBean.fieldNameArray(), - method.getName(), - dynFieldName); - action.init(); - if (action.templetKey.indexOf('{') < 0 && method.getParameterCount() > 0) { - // 一般有参数的方法,Cached.key应该是动态的 - logger.log( - Level.WARNING, - method + " has parameters but @" + Cached.class.getSimpleName() - + ".key not contains parameter"); - } + new CacheEntry(cached), method, serviceType, methodBean.fieldNameArray(), dynFieldName); actionMap.put(dynFieldName, action); } } } actionMap.forEach((field, action) -> { try { + resourceFactory.inject(action); + action.init(resourceFactory, service); + if (action.templetKey.indexOf('@') < 0 + && action.templetKey.indexOf('{') < 0 + && action.getMethod().getParameterCount() > 0) { + // 一般有参数的方法,Cached.key应该是动态的 + logger.log( + Level.WARNING, + action.getMethod() + " has parameters but @" + Cached.class.getSimpleName() + + ".key not contains parameter"); + } Field c = clazz.getDeclaredField(field); c.setAccessible(true); resourceFactory.inject(action); diff --git a/src/main/java/org/redkale/cache/spi/CacheEntry.java b/src/main/java/org/redkale/cache/spi/CacheEntry.java index 129745133..bdb02f29f 100644 --- a/src/main/java/org/redkale/cache/spi/CacheEntry.java +++ b/src/main/java/org/redkale/cache/spi/CacheEntry.java @@ -46,50 +46,26 @@ public class CacheEntry { return key; } - public void setKey(String key) { - this.key = key; - } - public String getHash() { return hash; } - public void setHash(String hash) { - this.hash = hash; - } - public String getLocalExpire() { return localExpire; } - public void setLocalExpire(String localExpire) { - this.localExpire = localExpire; - } - public String getRemoteExpire() { return remoteExpire; } - public void setRemoteExpire(String remoteExpire) { - this.remoteExpire = remoteExpire; - } - public TimeUnit getTimeUnit() { return timeUnit; } - public void setTimeUnit(TimeUnit timeUnit) { - this.timeUnit = timeUnit; - } - public boolean isNullable() { return nullable; } - public void setNullable(boolean nullable) { - this.nullable = nullable; - } - @Override public String toString() { return JsonConvert.root().convertTo(this); diff --git a/src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java b/src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java new file mode 100644 index 000000000..8671a3d55 --- /dev/null +++ b/src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java @@ -0,0 +1,39 @@ +/* + +*/ + +package org.redkale.cache.spi; + +/** + * 缓存key生成器 + * + * @see org.redkale.cache.Cached#key() + * + *

详情见: https://redkale.org + * + * @author zhangjx + * @since 2.8.0 + */ +public interface CacheKeyGenerator { + + /** + * 根据service和方法名生成key + * + * @param target service对象 + * @param action CacheAction对象 + * @param params 参数值 + * @return key值 + */ + public String generate(Object target, CacheAction action, Object... params); + + /** + * 生成器的名字 + * + * @see org.redkale.cache.Cached#key() + * + * @return name + */ + default String name() { + return ""; + } +} diff --git a/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java b/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java index bd8568f8a..56a700790 100644 --- a/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java +++ b/src/main/java/org/redkale/cache/spi/CacheModuleEngine.java @@ -3,18 +3,26 @@ */ package org.redkale.cache.spi; +import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; import org.redkale.asm.AsmMethodBoost; import org.redkale.boot.Application; import org.redkale.boot.ModuleEngine; import org.redkale.cache.CacheManager; +import org.redkale.inject.ResourceFactory; +import org.redkale.inject.ResourceTypeLoader; import org.redkale.service.Service; import org.redkale.util.AnyValue; import org.redkale.util.InstanceProvider; import org.redkale.util.RedkaleClassLoader; +import org.redkale.util.RedkaleException; /** @author zhangjx */ public class CacheModuleEngine extends ModuleEngine { @@ -70,6 +78,50 @@ public class CacheModuleEngine extends ModuleEngine { } } this.resourceFactory.register("", CacheManager.class, this.cacheManager); + ConcurrentHashMap generatorMap = new ConcurrentHashMap<>(); + this.resourceFactory.register(new ResourceTypeLoader() { + + @Override + public Object load( + ResourceFactory rf, + String srcResourceName, + Object srcObj, + String resourceName, + Field field, + Object attachment) { + try { + CacheKeyGenerator generator = rf.find(resourceName, CacheKeyGenerator.class); + if (generator == null) { + return generator; + } + generator = generatorMap.computeIfAbsent(resourceName, n -> { + for (CacheKeyGenerator instance : + ServiceLoader.load(CacheKeyGenerator.class, application.getClassLoader())) { + if (Objects.equals(n, instance.name())) { + rf.inject(instance); + if (instance instanceof Service) { + ((Service) instance).init(null); + } + return instance; + } + } + return null; + }); + if (generator != null) { + rf.register(resourceName, CacheKeyGenerator.class, generator); + } + return generator; + } catch (Exception e) { + logger.log(Level.SEVERE, CacheKeyGenerator.class.getSimpleName() + " inject error", e); + throw e instanceof RuntimeException ? (RuntimeException) e : new RedkaleException(e); + } + } + + @Override + public Type resourceType() { + return CacheKeyGenerator.class; + } + }); } /** 进入Application.shutdown方法被调用 */