增加CacheKeyGenerator功能

This commit is contained in:
redkale
2024-05-30 15:21:01 +08:00
parent ec9a0bbaf0
commit c94be39f02
6 changed files with 159 additions and 73 deletions

View File

@@ -3,18 +3,20 @@
*/ */
package org.redkale.cache; package org.redkale.cache;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.redkale.service.LoadMode; import org.redkale.service.LoadMode;
/** /**
* 标记在Service的缓存接口, 方法有以下限制: <br> * 标记在Service的缓存接口, 方法有以下限制: <br>
* 1、方法返回类型不能是void/CompletableFuture&#60;Void&#62; 2、方法返回类型必须可json序列化 3、方法必须是protected/public 4、方法不能是final/static * 1、方法返回类型不能是void/CompletableFuture&#60;Void&#62; <br>
* 2、方法返回类型必须可json序列化 <br>
* 3、方法必须是protected/public <br>
* 4、方法不能是final/static <br>
* *
* @since 2.8.0 * @since 2.8.0
*/ */
@@ -24,7 +26,10 @@ import org.redkale.service.LoadMode;
public @interface Cached { public @interface Cached {
/** /**
* 缓存的key支持参数动态组合比如"key_#{id}" * 缓存的key支持参数动态组合比如"key_#{id}" <br>
* '@'开头的key值视为CacheKeyGenerator对象名称 <br>
*
* @see org.redkale.cache.spi.CacheKeyGenerator#name()
* *
* @return 键 * @return 键
*/ */

View File

@@ -3,6 +3,7 @@
*/ */
package org.redkale.cache.spi; package org.redkale.cache.spi;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.time.Duration; import java.time.Duration;
@@ -13,6 +14,7 @@ import org.redkale.annotation.Nullable;
import org.redkale.annotation.Resource; import org.redkale.annotation.Resource;
import org.redkale.cache.CacheManager; import org.redkale.cache.CacheManager;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.inject.ResourceFactory;
import org.redkale.util.Environment; import org.redkale.util.Environment;
import org.redkale.util.MultiHashKey; import org.redkale.util.MultiHashKey;
import org.redkale.util.ThrowSupplier; import org.redkale.util.ThrowSupplier;
@@ -37,6 +39,8 @@ public class CacheAction {
private final CacheEntry cached; private final CacheEntry cached;
private final Method method;
// Supplier对象的类型 // Supplier对象的类型
private final Type resultType; private final Type resultType;
@@ -55,10 +59,6 @@ public class CacheAction {
// 获取动态的字段名 // 获取动态的字段名
private final String fieldName; private final String fieldName;
// 方法参数类型
@Nullable
private final Class[] paramTypes;
// 方法参数名 // 方法参数名
@Nullable @Nullable
private final String[] paramNames; private final String[] paramNames;
@@ -69,8 +69,11 @@ public class CacheAction {
// 模板key // 模板key
String templetKey; String templetKey;
// 缓存key // 缓存key生成器
private MultiHashKey dynKey; private CacheKeyGenerator keyGenerator;
// 父对象
private Object service;
// 本地缓存过期时长Duration.ZERO为永不过期为null表示不本地缓存 // 本地缓存过期时长Duration.ZERO为永不过期为null表示不本地缓存
private Duration localExpire; private Duration localExpire;
@@ -78,34 +81,33 @@ public class CacheAction {
// 远程缓存过期时长Duration.ZERO为永不过期为null表示不远程缓存 // 远程缓存过期时长Duration.ZERO为永不过期为null表示不远程缓存
private Duration remoteExpire; private Duration remoteExpire;
CacheAction( CacheAction(CacheEntry cached, Method method, Class serviceClass, String[] paramNames, String fieldName) {
CacheEntry cached,
Type returnType,
Class serviceClass,
Class[] paramTypes,
String[] paramNames,
String methodName,
String fieldName) {
this.cached = cached; this.cached = cached;
this.method = method;
this.nullable = cached.isNullable(); this.nullable = cached.isNullable();
this.serviceClass = Objects.requireNonNull(serviceClass); this.serviceClass = Objects.requireNonNull(serviceClass);
this.paramTypes = paramTypes;
this.paramNames = paramNames; this.paramNames = paramNames;
this.methodName = Objects.requireNonNull(methodName); this.methodName = method.getName();
this.fieldName = Objects.requireNonNull(fieldName); this.fieldName = Objects.requireNonNull(fieldName);
this.templetKey = cached.getKey(); this.templetKey = cached.getKey();
Type returnType = method.getGenericReturnType();
this.async = CompletableFuture.class.isAssignableFrom(TypeToken.typeToClass(returnType)); this.async = CompletableFuture.class.isAssignableFrom(TypeToken.typeToClass(returnType));
this.resultType = this.async ? ((ParameterizedType) returnType).getActualTypeArguments()[0] : 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()) this.hash = cached.getHash().trim().isEmpty() || CacheManager.DEFAULT_HASH.equals(cached.getHash())
? CacheManager.DEFAULT_HASH ? CacheManager.DEFAULT_HASH
: environment.getPropertyValue(cached.getHash()); : environment.getPropertyValue(cached.getHash());
String key = environment.getPropertyValue(cached.getKey()); String key = environment.getPropertyValue(cached.getKey());
this.templetKey = key; this.templetKey = key;
this.dynKey = MultiHashKey.create(paramNames, key); if (key.startsWith("@")) { // 动态加载缓存key生成器
this.localExpire = createDuration(cached.getLocalExpire()); 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()); this.remoteExpire = createDuration(cached.getRemoteExpire());
} }
@@ -114,10 +116,22 @@ public class CacheAction {
if (async) { if (async) {
ThrowSupplier supplier0 = supplier; ThrowSupplier supplier0 = supplier;
return (T) manager.bothGetSetAsync( 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 { } else {
return manager.bothGetSet( 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 @Override
public String toString() { public String toString() {
return "{" return "{"
+ "\"serviceClass\":" + serviceClass.getName() + "\"serviceClass\":" + serviceClass.getName()
+ ",\"methodName\":\"" + methodName + "\"" + ",\"methodName\":\"" + methodName + "\""
+ ",\"fieldName\":\"" + fieldName + "\"" + ",\"fieldName\":\"" + fieldName + "\""
+ ",\"paramTypes\":" + JsonConvert.root().convertTo(paramTypes) + ",\"paramTypes\":" + JsonConvert.root().convertTo(method.getParameterTypes())
+ ",\"paramNames\":" + JsonConvert.root().convertTo(paramNames) + ",\"paramNames\":" + JsonConvert.root().convertTo(paramNames)
+ ",\"templetKey\":\"" + templetKey + "\""
+ ",\"resultType\":\"" + resultType + "\"" + ",\"resultType\":\"" + resultType + "\""
+ ",\"cache\":" + cached + ",\"cache\":" + cached
+ "}"; + "}";

View File

@@ -169,13 +169,7 @@ public class CacheAsmMethodBoost extends AsmMethodBoost {
mv.visitMaxs(20, 20); mv.visitMaxs(20, 20);
mv.visitEnd(); mv.visitEnd();
CacheAction action = new CacheAction( CacheAction action = new CacheAction(
new CacheEntry(cached), new CacheEntry(cached), method, serviceType, methodBean.fieldNameArray(), dynFieldName);
method.getGenericReturnType(),
serviceType,
method.getParameterTypes(),
methodBean.fieldNameArray(),
method.getName(),
dynFieldName);
actions.put(dynFieldName, action); actions.put(dynFieldName, action);
} }
{ // ThrowSupplier { // ThrowSupplier
@@ -223,27 +217,24 @@ public class CacheAsmMethodBoost extends AsmMethodBoost {
String dynFieldName = cached.dynField(); String dynFieldName = cached.dynField();
AsmMethodBean methodBean = AsmMethodBean.get(methodBeans, method); AsmMethodBean methodBean = AsmMethodBean.get(methodBeans, method);
CacheAction action = new CacheAction( CacheAction action = new CacheAction(
new CacheEntry(cached), new CacheEntry(cached), method, serviceType, methodBean.fieldNameArray(), dynFieldName);
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");
}
actionMap.put(dynFieldName, action); actionMap.put(dynFieldName, action);
} }
} }
} }
actionMap.forEach((field, action) -> { actionMap.forEach((field, action) -> {
try { 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); Field c = clazz.getDeclaredField(field);
c.setAccessible(true); c.setAccessible(true);
resourceFactory.inject(action); resourceFactory.inject(action);

View File

@@ -46,50 +46,26 @@ public class CacheEntry {
return key; return key;
} }
public void setKey(String key) {
this.key = key;
}
public String getHash() { public String getHash() {
return hash; return hash;
} }
public void setHash(String hash) {
this.hash = hash;
}
public String getLocalExpire() { public String getLocalExpire() {
return localExpire; return localExpire;
} }
public void setLocalExpire(String localExpire) {
this.localExpire = localExpire;
}
public String getRemoteExpire() { public String getRemoteExpire() {
return remoteExpire; return remoteExpire;
} }
public void setRemoteExpire(String remoteExpire) {
this.remoteExpire = remoteExpire;
}
public TimeUnit getTimeUnit() { public TimeUnit getTimeUnit() {
return timeUnit; return timeUnit;
} }
public void setTimeUnit(TimeUnit timeUnit) {
this.timeUnit = timeUnit;
}
public boolean isNullable() { public boolean isNullable() {
return nullable; return nullable;
} }
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
@Override @Override
public String toString() { public String toString() {
return JsonConvert.root().convertTo(this); return JsonConvert.root().convertTo(this);

View File

@@ -0,0 +1,39 @@
/*
*/
package org.redkale.cache.spi;
/**
* 缓存key生成器
*
* @see org.redkale.cache.Cached#key()
*
* <p>详情见: 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 "";
}
}

View File

@@ -3,18 +3,26 @@
*/ */
package org.redkale.cache.spi; package org.redkale.cache.spi;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.redkale.asm.AsmMethodBoost; import org.redkale.asm.AsmMethodBoost;
import org.redkale.boot.Application; import org.redkale.boot.Application;
import org.redkale.boot.ModuleEngine; import org.redkale.boot.ModuleEngine;
import org.redkale.cache.CacheManager; import org.redkale.cache.CacheManager;
import org.redkale.inject.ResourceFactory;
import org.redkale.inject.ResourceTypeLoader;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.AnyValue; import org.redkale.util.AnyValue;
import org.redkale.util.InstanceProvider; import org.redkale.util.InstanceProvider;
import org.redkale.util.RedkaleClassLoader; import org.redkale.util.RedkaleClassLoader;
import org.redkale.util.RedkaleException;
/** @author zhangjx */ /** @author zhangjx */
public class CacheModuleEngine extends ModuleEngine { public class CacheModuleEngine extends ModuleEngine {
@@ -70,6 +78,50 @@ public class CacheModuleEngine extends ModuleEngine {
} }
} }
this.resourceFactory.register("", CacheManager.class, this.cacheManager); this.resourceFactory.register("", CacheManager.class, this.cacheManager);
ConcurrentHashMap<String, CacheKeyGenerator> 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方法被调用 */ /** 进入Application.shutdown方法被调用 */