增加CacheKeyGenerator功能
This commit is contained in:
15
src/main/java/org/redkale/cache/Cached.java
vendored
15
src/main/java/org/redkale/cache/Cached.java
vendored
@@ -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<Void> 2、方法返回类型必须可json序列化 3、方法必须是protected/public 4、方法不能是final/static
|
* 1、方法返回类型不能是void/CompletableFuture<Void> <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 键
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
+ "}";
|
+ "}";
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
39
src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java
vendored
Normal file
39
src/main/java/org/redkale/cache/spi/CacheKeyGenerator.java
vendored
Normal 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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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方法被调用 */
|
||||||
|
|||||||
Reference in New Issue
Block a user