MultiHashKey

This commit is contained in:
redkale
2023-12-21 10:20:05 +08:00
parent f9ec08adea
commit cda78cfc7d
5 changed files with 349 additions and 96 deletions

View File

@@ -17,7 +17,7 @@ import org.redkale.convert.json.JsonConvert;
import org.redkale.net.sncp.Sncp;
import org.redkale.util.Environment;
import org.redkale.util.TypeToken;
import org.redkale.util.CombinedKey;
import org.redkale.util.MultiHashKey;
/**
*
@@ -70,7 +70,7 @@ public class CacheAction {
private String key;
//缓存的key
private CombinedKey dynKey;
private MultiHashKey dynKey;
//本地缓存过期时长
private Duration localExpire;
@@ -94,7 +94,7 @@ public class CacheAction {
? Sncp.getResourceType(serviceClass).getSimpleName()
: environment.getPropertyValue(cached.hash());
this.key = environment.getPropertyValue(cached.key());
this.dynKey = CombinedKey.create(paramTypes, paramNames, key);
this.dynKey = MultiHashKey.create(paramNames, key);
this.localExpire = createDuration(cached.localExpire());
this.remoteExpire = createDuration(cached.remoteExpire());
}

View File

@@ -1,87 +0,0 @@
/*
*
*/
package org.redkale.util;
import java.util.Objects;
/**
*
* @author zhangjx
*/
class CombinedKeys {
public static CombinedKey create(Class[] paramTypes, String[] paramNames, String key) {
Objects.requireNonNull(key, "key for " + CombinedKey.class.getSimpleName() + " is null");
if ((paramTypes != null && paramNames != null && paramTypes.length != paramNames.length)
|| (paramTypes == null && paramNames != null)
|| (paramTypes != null && paramNames == null)) {
throw new IllegalArgumentException("paramTypes.length and paramNames.length is inconsistent");
}
if (key.indexOf('{') < 0) {
return new StringDynamicKey(key);
} else {
if (paramNames != null) {
for (int i = 0; i < paramNames.length; i++) {
if (key.equalsIgnoreCase("#{" + paramNames[i] + "}")) {
return new ParamDynamicKey(i);
}
}
}
return new CombinedDynamicKey(paramTypes, paramNames, key);
}
}
static class CombinedDynamicKey implements CombinedKey {
private final CombinedKey[] keys;
public CombinedDynamicKey(Class[] paramTypes, String[] paramNames, String key) {
this.keys = new CombinedKey[0];
}
@Override
public String keyFor(Object... args) {
StringBuilder sb = new StringBuilder();
for (CombinedKey key : keys) {
sb.append(key.keyFor(args));
}
return sb.toString();
}
}
static class ParamDynamicKey implements CombinedKey {
private final int index;
public ParamDynamicKey(int index) {
this.index = index;
}
@Override
public String keyFor(Object... args) {
return String.valueOf(args[index]);
}
}
static class StringDynamicKey implements CombinedKey {
private final String key;
public StringDynamicKey(String key) {
this.key = key;
}
@Override
public String keyFor(Object... args) {
return key;
}
}
private CombinedKeys() {
//do nothing
}
}

View File

@@ -14,20 +14,19 @@ package org.redkale.util;
*
* @since 2.8.0
*/
public interface CombinedKey {
public interface MultiHashKey {
public String keyFor(Object... args);
/**
* 生成Key, paramTypes与paramNames长度必须一致
* key只支持带#{}的表达式 且不能嵌套, :name_#{key_#{id}}
*
* @param paramTypes 参数类型
* @param paramNames 参数名
* @param key key表达式
*
* @return CombinedKey
* @return MultiHashKey
*/
public static CombinedKey create(Class[] paramTypes, String[] paramNames, String key) {
return CombinedKeys.create(paramTypes, paramNames, key);
public static MultiHashKey create(String[] paramNames, String key) {
return MultiHashKeys.create(paramNames, key);
}
}

View File

@@ -0,0 +1,213 @@
/*
*
*/
package org.redkale.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author zhangjx
*/
class MultiHashKeys {
public static MultiHashKey create(String[] paramNames, String key) {
Objects.requireNonNull(key, "key for " + MultiHashKey.class.getSimpleName() + " is null");
if (key.indexOf('{') < 0) { //字符串常量
return new StringKey(key);
} else {
Objects.requireNonNull(paramNames, "paramNames for " + MultiHashKey.class.getSimpleName() + " is null");
char last = 0;
boolean paraming = false;
char[] chars = key.toCharArray();
StringBuilder sb = new StringBuilder();
List<MultiHashKey> list = new ArrayList<>();
for (int i = 0; i < chars.length; i++) {
char ch = chars[i];
if (ch == '{') {
if (paraming || i < 1) {
throw new RedkaleException(MultiHashKey.class.getSimpleName() + " parse error, key: " + key);
}
if (last == '#') {
String name = sb.substring(0, sb.length() - 1);
if (!name.isEmpty()) {
list.add(new StringKey(name));
}
sb.delete(0, sb.length());
paraming = true;
} else {
throw new RedkaleException(MultiHashKey.class.getSimpleName() + " parse error, key: " + key);
}
} else if (ch == '}') {
if (!paraming) {
throw new RedkaleException(MultiHashKey.class.getSimpleName() + " parse error, key: " + key);
}
String name = sb.toString();
sb.delete(0, sb.length());
if (name.indexOf('.') > 0) {
list.add(new ParamsKey(paramNames, name));
} else {
list.add(new ParamKey(paramNames, name));
}
paraming = false;
} else {
sb.append(ch);
}
last = ch;
}
if (list.size() == 1) {
return list.get(0);
}
return new ArrayKey(list.toArray(new MultiHashKey[list.size()]));
}
}
static class ArrayKey implements MultiHashKey {
private final MultiHashKey[] keys;
public ArrayKey(MultiHashKey[] keys) {
this.keys = keys;
}
@Override
public String keyFor(Object... args) {
StringBuilder sb = new StringBuilder();
for (MultiHashKey key : keys) {
sb.append(key.keyFor(args));
}
return sb.toString();
}
@Override
public String toString() {
return ArrayKey.class.getSimpleName() + Arrays.toString(keys);
}
}
static class ParamsKey implements MultiHashKey {
private static final ConcurrentHashMap<String, Attribute> attrCache = new ConcurrentHashMap<>();
private final int index;
private final String fullField;
private final String[] fields;
public ParamsKey(int index, String fullField) {
this.index = index;
this.fullField = fullField;
this.fields = fullField.split("\\.");
}
public ParamsKey(String[] paramNames, String fullField) {
int rs = -1;
for (int i = 0; i < paramNames.length; i++) {
if (fullField.startsWith(paramNames[i] + '.')) {
rs = i;
break;
}
}
if (rs < 0) {
throw new RedkaleException(fullField + " not found in " + Arrays.toString(paramNames));
}
this.index = rs;
this.fullField = fullField;
this.fields = fullField.split("\\.");
}
@Override
public String keyFor(Object... args) {
return String.valueOf(get(args[index]));
}
private Object get(Object val) {
if (val == null) {
return val;
}
String[] subs = fields;
for (int i = 1; i < subs.length; i++) {
String fieldName = subs[i];
Class clz = val.getClass();
Attribute attr = attrCache.computeIfAbsent(clz.getName() + ":" + fieldName, k -> Attribute.create(clz, fieldName));
val = attr.get(val);
if (val == null) {
return val;
}
}
return val;
}
@Override
public String toString() {
return ParamsKey.class.getSimpleName() + "{field: " + fullField + ", index: " + index + "}";
}
}
static class ParamKey implements MultiHashKey {
private final int index;
private final String field;
public ParamKey(int index, String field) {
this.index = index;
this.field = field;
}
public ParamKey(String[] paramNames, String field) {
int rs = -1;
for (int i = 0; i < paramNames.length; i++) {
if (field.equalsIgnoreCase(paramNames[i])) {
rs = i;
break;
}
}
if (rs < 0) {
throw new RedkaleException(field + " not found in " + Arrays.toString(paramNames));
}
this.index = rs;
this.field = field;
}
@Override
public String keyFor(Object... args) {
return String.valueOf(args[index]);
}
@Override
public String toString() {
return ParamKey.class.getSimpleName() + "{field: " + field + ", index: " + index + "}";
}
}
static class StringKey implements MultiHashKey {
private final String key;
public StringKey(String key) {
this.key = key;
}
@Override
public String keyFor(Object... args) {
return key;
}
@Override
public String toString() {
return StringKey.class.getSimpleName() + "{key: " + key + "}";
}
}
private MultiHashKeys() {
//do nothing
}
}

View File

@@ -0,0 +1,128 @@
/*
*
*/
package org.redkale.test.util;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.redkale.util.MultiHashKey;
import org.redkale.util.Utility;
/**
*
* @author zhangjx
*/
public class MultiHashKeyTest {
public static void main(String[] args) throws Throwable {
MultiHashKeyTest test = new MultiHashKeyTest();
test.run1();
test.run2();
test.run3();
test.run4();
test.run5();
test.run6();
test.run7();
test.run8();
test.run9();
}
@Test
public void run1() throws Exception {
String[] paramNames = {"name", "id"};
String key = "#{name}";
MultiHashKey rs = MultiHashKey.create(paramNames, key);
System.out.println(rs);
Assertions.assertEquals("ParamKey{field: name, index: 0}", rs.toString());
Assertions.assertEquals("haha", rs.keyFor("haha", 123));
}
@Test
public void run2() throws Exception {
String[] paramNames = {"name", "id"};
String key = "#{id}";
MultiHashKey rs = MultiHashKey.create(paramNames, key);
System.out.println(rs);
Assertions.assertEquals("ParamKey{field: id, index: 1}", rs.toString());
Assertions.assertEquals("123", rs.keyFor("haha", 123));
}
@Test
public void run3() throws Exception {
String[] paramNames = {"name", "id"};
String key = "name";
MultiHashKey rs = MultiHashKey.create(paramNames, key);
System.out.println(rs);
Assertions.assertEquals("StringKey{key: name}", rs.toString());
Assertions.assertEquals("name", rs.keyFor("haha", 123));
}
@Test
public void run4() throws Exception {
String[] paramNames = {"name", "id"};
String key = "key_#{name}_#{id}_#{name.index}";
MultiHashKey rs = MultiHashKey.create(paramNames, key);
System.out.println(rs);
Assertions.assertEquals("ArrayKey[StringKey{key: key_}, ParamKey{field: name, index: 0}, StringKey{key: _}, "
+ "ParamKey{field: id, index: 1}, StringKey{key: _}, "
+ "ParamsKey{field: name.index, index: 0}]", rs.toString());
Assertions.assertEquals("key_n124_123_124", rs.keyFor(new Name(124), 123));
}
@Test
public void run5() throws Exception {
String[] paramNames = {"map", "id"};
String key = "key_#{map.name}_#{id}_#{map.index}";
MultiHashKey rs = MultiHashKey.create(paramNames, key);
System.out.println(rs);
Assertions.assertEquals("ArrayKey[StringKey{key: key_}, "
+ "ParamsKey{field: map.name, index: 0}, StringKey{key: _}, "
+ "ParamKey{field: id, index: 1}, StringKey{key: _}, "
+ "ParamsKey{field: map.index, index: 0}]", rs.toString());
Map<String, Object> map = Utility.ofMap("name", "me", "index", 123);
Assertions.assertEquals("key_me_123_123", rs.keyFor(map, 123));
}
@Test
public void run6() throws Exception {
}
@Test
public void run7() throws Exception {
}
@Test
public void run8() throws Exception {
}
@Test
public void run9() throws Exception {
}
public static class Name {
private int index;
public Name(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String toString() {
return "n" + index;
}
}
}