diff --git a/src/main/java/org/redkale/cache/spi/CacheAction.java b/src/main/java/org/redkale/cache/spi/CacheAction.java index 9e340d4f1..653d346ab 100644 --- a/src/main/java/org/redkale/cache/spi/CacheAction.java +++ b/src/main/java/org/redkale/cache/spi/CacheAction.java @@ -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()); } diff --git a/src/main/java/org/redkale/util/CombinedKeys.java b/src/main/java/org/redkale/util/CombinedKeys.java deleted file mode 100644 index 337b2be13..000000000 --- a/src/main/java/org/redkale/util/CombinedKeys.java +++ /dev/null @@ -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 - } - -} diff --git a/src/main/java/org/redkale/util/CombinedKey.java b/src/main/java/org/redkale/util/MultiHashKey.java similarity index 50% rename from src/main/java/org/redkale/util/CombinedKey.java rename to src/main/java/org/redkale/util/MultiHashKey.java index f9b612764..71ecc1965 100644 --- a/src/main/java/org/redkale/util/CombinedKey.java +++ b/src/main/java/org/redkale/util/MultiHashKey.java @@ -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); } } diff --git a/src/main/java/org/redkale/util/MultiHashKeys.java b/src/main/java/org/redkale/util/MultiHashKeys.java new file mode 100644 index 000000000..bc85daf97 --- /dev/null +++ b/src/main/java/org/redkale/util/MultiHashKeys.java @@ -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 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 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 + } + +} diff --git a/src/test/java/org/redkale/test/util/MultiHashKeyTest.java b/src/test/java/org/redkale/test/util/MultiHashKeyTest.java new file mode 100644 index 000000000..63ad86f64 --- /dev/null +++ b/src/test/java/org/redkale/test/util/MultiHashKeyTest.java @@ -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 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; + } + } + +}