mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-17 18:14:50 +00:00
ref[storage]: refactor lambda of storage
This commit is contained in:
1 parent
a6f06579b1
commit
43f2e608b2
5 files changed
+288
-241
No files matched your search
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zfoo.storage.manager;
|
||||||
|
|
||||||
|
import com.zfoo.protocol.collection.CollectionUtils;
|
||||||
|
import com.zfoo.protocol.util.*;
|
||||||
|
import com.zfoo.storage.interpreter.ResourceInterpreter;
|
||||||
|
import com.zfoo.storage.model.IStorage;
|
||||||
|
import com.zfoo.storage.model.IdDef;
|
||||||
|
import com.zfoo.storage.model.IndexDef;
|
||||||
|
import com.zfoo.storage.util.function.Func1;
|
||||||
|
import com.zfoo.storage.util.lambda.*;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author godotg
|
||||||
|
*/
|
||||||
|
public abstract class AbstractStorage<K, V> implements IStorage<K, V> {
|
||||||
|
// 非唯一索引
|
||||||
|
protected Map<String, Map<Object, List<V>>> indexMap = new HashMap<>();
|
||||||
|
// 唯一索引
|
||||||
|
protected Map<String, Map<Object, V>> uniqueIndexMap = new HashMap<>();
|
||||||
|
|
||||||
|
protected Class<?> clazz;
|
||||||
|
protected IdDef idDef;
|
||||||
|
protected Map<String, IndexDef> indexDefMap;
|
||||||
|
// EN: unused configuration tables will clear data to save memory.
|
||||||
|
// CN: 没有被使用的配置表会清除data数据,以达到节省内存的目的
|
||||||
|
protected boolean recycle = true;
|
||||||
|
|
||||||
|
private ConcurrentReferenceHashMap<Func1<V, ?>, String> funcCaches = new ConcurrentReferenceHashMap<>();
|
||||||
|
|
||||||
|
public static AbstractStorage<?, ?> parse(InputStream inputStream, Class<?> resourceClazz, String suffix) {
|
||||||
|
var idDef = IdDef.valueOf(resourceClazz);
|
||||||
|
var indexDefMap = Collections.unmodifiableMap(IndexDef.createResourceIndexes(resourceClazz));
|
||||||
|
|
||||||
|
try {
|
||||||
|
var list = ResourceInterpreter.read(inputStream, resourceClazz, suffix);
|
||||||
|
// 校验id是否重复
|
||||||
|
var set = new HashSet<>();
|
||||||
|
for (var value : list) {
|
||||||
|
var id = ReflectionUtils.getField(idDef.getField(), value);
|
||||||
|
if (id == null) {
|
||||||
|
throw new RuntimeException("There is an item with an unconfigured id in the static resource");
|
||||||
|
}
|
||||||
|
if (set.contains(id)) {
|
||||||
|
throw new RuntimeException(StringUtils.format("Duplicate [id:{}] of static resource [resource:{}]", id, resourceClazz.getSimpleName()));
|
||||||
|
}
|
||||||
|
set.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var idType = idDef.getField().getType();
|
||||||
|
if (idType == int.class || idType == Integer.class) {
|
||||||
|
return new StorageInt<>(resourceClazz, idDef, indexDefMap, list);
|
||||||
|
} else if (idType == long.class || idType == Long.class) {
|
||||||
|
return new StorageLong<>(resourceClazz, idDef, indexDefMap, list);
|
||||||
|
} else {
|
||||||
|
return new StorageObject<>(resourceClazz, idDef, indexDefMap, list);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeIO(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractStorage(Class<?> clazz, IdDef idDef, Map<String, IndexDef> indexDefMap, List<?> values) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.idDef = idDef;
|
||||||
|
this.indexDefMap = indexDefMap;
|
||||||
|
for (var value : values) {
|
||||||
|
// 添加资源
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
var v = (V) value;
|
||||||
|
// 添加索引
|
||||||
|
for (var def : indexDefMap.values()) {
|
||||||
|
// 使用field的名称作为索引的名称
|
||||||
|
var indexKey = def.getField().getName();
|
||||||
|
var indexValue = ReflectionUtils.getField(def.getField(), v);
|
||||||
|
if (def.isUnique()) {
|
||||||
|
var uniqueIndex = uniqueIndexMap.computeIfAbsent(indexKey, it -> new HashMap<>(values.size()));
|
||||||
|
if (uniqueIndex.put(indexValue, v) != null) {
|
||||||
|
throw new RuntimeException(StringUtils.format("Duplicate unique index [index:{}][value:{}] of static resource [class:{}]", indexKey, indexValue, clazz.getName()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var index = indexMap.computeIfAbsent(indexKey, it -> new HashMap<>(values.size()));
|
||||||
|
var list = index.computeIfAbsent(indexValue, it -> new ArrayList<V>());
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <INDEX> List<V> getIndexes(Func1<V, INDEX> func, INDEX index) {
|
||||||
|
String indexName = getMethodToField(func);
|
||||||
|
var indexValues = indexMap.get(indexName);
|
||||||
|
AssertionUtils.notNull(indexValues, "The index of [indexName:{}] does not exist in the static resource [resource:{}]", indexName, clazz.getSimpleName());
|
||||||
|
var values = indexValues.get(index);
|
||||||
|
if (CollectionUtils.isEmpty(values)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <INDEX> V getUniqueIndex(Func1<V, INDEX> func, INDEX index) {
|
||||||
|
String uniqueIndexName = getMethodToField(func);
|
||||||
|
var indexValueMap = uniqueIndexMap.get(uniqueIndexName);
|
||||||
|
AssertionUtils.notNull(indexValueMap, "There is no a unique index for [uniqueIndexName:{}] in the static resource [resource:{}]", uniqueIndexName, clazz.getSimpleName());
|
||||||
|
var value = indexValueMap.get(index);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recycleStorage() {
|
||||||
|
recycle = true;
|
||||||
|
indexMap = null;
|
||||||
|
uniqueIndexMap = null;
|
||||||
|
idDef = null;
|
||||||
|
indexDefMap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecycle() {
|
||||||
|
return recycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRecycle(boolean recycle) {
|
||||||
|
this.recycle = recycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdDef getIdDef() {
|
||||||
|
return idDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <INDEX> String getMethodToField(Func1<V, INDEX> func) {
|
||||||
|
var indexName = funcCaches.get(func);
|
||||||
|
if (indexName != null) {
|
||||||
|
return indexName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. IDEA 调试模式下 lambda 表达式是一个代理
|
||||||
|
if (func instanceof Proxy) {
|
||||||
|
try {
|
||||||
|
var lambda = new IdeaProxyLambdaMeta((Proxy) func);
|
||||||
|
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 反射读取
|
||||||
|
if (indexName == null) {
|
||||||
|
try {
|
||||||
|
var method = func.getClass().getDeclaredMethod("writeReplace");
|
||||||
|
ReflectionUtils.makeAccessible(method);
|
||||||
|
var lambda = new ReflectLambdaMeta((java.lang.invoke.SerializedLambda) method.invoke(func));
|
||||||
|
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||||
|
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 反射失败使用序列化的方式读取
|
||||||
|
if (indexName == null) {
|
||||||
|
try {
|
||||||
|
var lambda = new ShadowLambdaMeta(SerializedLambda.extract(func));
|
||||||
|
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 通过将func带入到dataMap中求解,适合GraalVM环境中
|
||||||
|
if (indexName == null) {
|
||||||
|
try {
|
||||||
|
var fields = clazz.getDeclaredFields();
|
||||||
|
Arrays.stream(fields).forEach(ReflectionUtils::makeAccessible);
|
||||||
|
for (var value : getAll()) {
|
||||||
|
var r = func.apply(value);
|
||||||
|
var valueFields = Arrays.stream(fields)
|
||||||
|
.map(it -> ReflectionUtils.getField(it, value))
|
||||||
|
.filter(it -> it.equals(r) && it.getClass() == r.getClass())
|
||||||
|
.toList();
|
||||||
|
// 如果只有一个能匹配到func的返回值则就是这个方法
|
||||||
|
if (valueFields.size() == 1) {
|
||||||
|
for (var field : fields) {
|
||||||
|
if (!ReflectionUtils.getField(field, value).equals(r)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
indexName = field.getName();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
funcCaches.put(func, indexName);
|
||||||
|
return indexName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,33 +13,38 @@
|
|||||||
package com.zfoo.storage.manager;
|
package com.zfoo.storage.manager;
|
||||||
|
|
||||||
import com.zfoo.protocol.util.AssertionUtils;
|
import com.zfoo.protocol.util.AssertionUtils;
|
||||||
|
import com.zfoo.protocol.util.ReflectionUtils;
|
||||||
|
import com.zfoo.protocol.util.StringUtils;
|
||||||
|
import com.zfoo.storage.model.IdDef;
|
||||||
|
import com.zfoo.storage.model.IndexDef;
|
||||||
import io.netty.util.collection.IntObjectHashMap;
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author godotg
|
* @author godotg
|
||||||
*/
|
*/
|
||||||
public class StorageInt<K, V> extends StorageObject<K, V> {
|
public class StorageInt<K, V> extends AbstractStorage<K, V> {
|
||||||
|
|
||||||
private IntObjectHashMap<V> dataMap;
|
private IntObjectHashMap<V> dataMap;
|
||||||
|
|
||||||
public StorageInt(StorageObject<K, V> storage) {
|
public StorageInt(Class<?> clazz, IdDef idDef, Map<String, IndexDef> indexDefMap, List<?> values) {
|
||||||
this.dataMap = new IntObjectHashMap<V>(storage.size());
|
super(clazz, idDef, indexDefMap, values);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var map = (Map<? extends Integer, ? extends V>) storage.getData();
|
this.dataMap = new IntObjectHashMap<>(values.size());
|
||||||
this.dataMap.putAll(map);
|
for (var value : values) {
|
||||||
super.indexMap = storage.indexMap;
|
@SuppressWarnings("unchecked")
|
||||||
super.uniqueIndexMap = storage.uniqueIndexMap;
|
var id = (K) ReflectionUtils.getField(idDef.getField(), value);
|
||||||
super.clazz = storage.clazz;
|
@SuppressWarnings("unchecked")
|
||||||
super.idDef = storage.idDef;
|
var v = (V) value;
|
||||||
super.indexDefMap = storage.indexDefMap;
|
dataMap.put((Integer) id, v);
|
||||||
super.recycle = storage.recycle;
|
}
|
||||||
storage.recycleStorage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contain(K id) {
|
public boolean contain(K id) {
|
||||||
return contain((int) id);
|
return contain((int) id);
|
||||||
|
|||||||
@@ -13,33 +13,38 @@
|
|||||||
package com.zfoo.storage.manager;
|
package com.zfoo.storage.manager;
|
||||||
|
|
||||||
import com.zfoo.protocol.util.AssertionUtils;
|
import com.zfoo.protocol.util.AssertionUtils;
|
||||||
|
import com.zfoo.protocol.util.ReflectionUtils;
|
||||||
|
import com.zfoo.storage.model.IdDef;
|
||||||
|
import com.zfoo.storage.model.IndexDef;
|
||||||
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
import io.netty.util.collection.LongObjectHashMap;
|
import io.netty.util.collection.LongObjectHashMap;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author godotg
|
* @author godotg
|
||||||
*/
|
*/
|
||||||
public class StorageLong<K, V> extends StorageObject<K, V> {
|
public class StorageLong<K, V> extends AbstractStorage<K, V> {
|
||||||
|
|
||||||
private LongObjectHashMap<V> dataMap;
|
private LongObjectHashMap<V> dataMap;
|
||||||
|
|
||||||
public StorageLong(StorageObject<K, V> storage) {
|
public StorageLong(Class<?> clazz, IdDef idDef, Map<String, IndexDef> indexDefMap, List<?> values) {
|
||||||
this.dataMap = new LongObjectHashMap<V>(storage.size());
|
super(clazz, idDef, indexDefMap, values);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var map = (Map<? extends Long, ? extends V>) storage.getData();
|
this.dataMap = new LongObjectHashMap<>(values.size());
|
||||||
this.dataMap.putAll(map);
|
for (var value : values) {
|
||||||
super.indexMap = storage.indexMap;
|
@SuppressWarnings("unchecked")
|
||||||
super.uniqueIndexMap = storage.uniqueIndexMap;
|
var id = (K) ReflectionUtils.getField(idDef.getField(), value);
|
||||||
super.clazz = storage.clazz;
|
@SuppressWarnings("unchecked")
|
||||||
super.idDef = storage.idDef;
|
var v = (V) value;
|
||||||
super.indexDefMap = storage.indexDefMap;
|
dataMap.put((Long) id, v);
|
||||||
super.recycle = storage.recycle;
|
}
|
||||||
storage.recycleStorage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contain(K id) {
|
public boolean contain(K id) {
|
||||||
return contain((long) id);
|
return contain((long) id);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public class StorageManager implements IStorageManager {
|
|||||||
var clazz = definition.getClazz();
|
var clazz = definition.getClazz();
|
||||||
var resource = definition.getResource();
|
var resource = definition.getResource();
|
||||||
var fileExtName = FileUtils.fileExtName(resource.getFilename());
|
var fileExtName = FileUtils.fileExtName(resource.getFilename());
|
||||||
var storage = StorageObject.parse(resource.getInputStream(), clazz, fileExtName);
|
var storage = AbstractStorage.parse(resource.getInputStream(), clazz, fileExtName);
|
||||||
storageMap.putIfAbsent(clazz, storage);
|
storageMap.putIfAbsent(clazz, storage);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -13,201 +13,76 @@
|
|||||||
package com.zfoo.storage.manager;
|
package com.zfoo.storage.manager;
|
||||||
|
|
||||||
import com.zfoo.protocol.collection.CollectionUtils;
|
import com.zfoo.protocol.collection.CollectionUtils;
|
||||||
import com.zfoo.protocol.util.*;
|
import com.zfoo.protocol.util.AssertionUtils;
|
||||||
import com.zfoo.storage.interpreter.ResourceInterpreter;
|
import com.zfoo.protocol.util.ReflectionUtils;
|
||||||
import com.zfoo.storage.model.IStorage;
|
|
||||||
import com.zfoo.storage.model.IdDef;
|
import com.zfoo.storage.model.IdDef;
|
||||||
import com.zfoo.storage.model.IndexDef;
|
import com.zfoo.storage.model.IndexDef;
|
||||||
import com.zfoo.storage.util.function.Func1;
|
import io.netty.util.collection.LongObjectHashMap;
|
||||||
import com.zfoo.storage.util.lambda.*;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author godotg
|
* @author godotg
|
||||||
*/
|
*/
|
||||||
public class StorageObject<K, V> implements IStorage<K, V> {
|
public class StorageObject<K, V> extends AbstractStorage<K, V> {
|
||||||
// all storage data
|
|
||||||
private Map<K, V> dataMap;
|
private Map<K, V> dataMap;
|
||||||
// 非唯一索引
|
|
||||||
protected Map<String, Map<Object, List<V>>> indexMap = new HashMap<>();
|
|
||||||
// 唯一索引
|
|
||||||
protected Map<String, Map<Object, V>> uniqueIndexMap = new HashMap<>();
|
|
||||||
|
|
||||||
protected Class<?> clazz;
|
|
||||||
protected IdDef idDef;
|
|
||||||
protected Map<String, IndexDef> indexDefMap;
|
|
||||||
// EN: unused configuration tables will clear data to save memory.
|
|
||||||
// CN: 没有被使用的配置表会清除data数据,以达到节省内存的目的
|
|
||||||
protected boolean recycle = true;
|
|
||||||
|
|
||||||
private ConcurrentReferenceHashMap<Func1<V, ?>, String> funcCaches = new ConcurrentReferenceHashMap<>();
|
|
||||||
|
|
||||||
public static StorageObject<?, ?> parse(InputStream inputStream, Class<?> resourceClazz, String suffix) {
|
|
||||||
var idDef = IdDef.valueOf(resourceClazz);
|
|
||||||
var indexDefMap = Collections.unmodifiableMap(IndexDef.createResourceIndexes(resourceClazz));
|
|
||||||
|
|
||||||
try {
|
|
||||||
var list = ResourceInterpreter.read(inputStream, resourceClazz, suffix);
|
|
||||||
var storage = new StorageObject<>(resourceClazz, idDef, indexDefMap, list);
|
|
||||||
|
|
||||||
var idType = idDef.getField().getType();
|
|
||||||
if (idType == int.class || idType == Integer.class) {
|
|
||||||
return new StorageInt<>(storage);
|
|
||||||
} else if (idType == long.class || idType == Long.class) {
|
|
||||||
return new StorageLong<>(storage);
|
|
||||||
} else {
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeIO(inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StorageObject() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public StorageObject(Class<?> clazz, IdDef idDef, Map<String, IndexDef> indexDefMap, List<?> values) {
|
public StorageObject(Class<?> clazz, IdDef idDef, Map<String, IndexDef> indexDefMap, List<?> values) {
|
||||||
|
super(clazz, idDef, indexDefMap, values);
|
||||||
this.dataMap = new HashMap<>(CollectionUtils.capacity(values.size()));
|
this.dataMap = new HashMap<>(CollectionUtils.capacity(values.size()));
|
||||||
this.clazz = clazz;
|
|
||||||
this.idDef = idDef;
|
|
||||||
this.indexDefMap = indexDefMap;
|
|
||||||
for (var value : values) {
|
for (var value : values) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var id = (K) ReflectionUtils.getField(idDef.getField(), value);
|
var id = (K) ReflectionUtils.getField(idDef.getField(), value);
|
||||||
|
|
||||||
if (id == null) {
|
|
||||||
throw new RuntimeException("There is an item with an unconfigured id in the static resource");
|
|
||||||
}
|
|
||||||
if (dataMap.containsKey(id)) {
|
|
||||||
throw new RuntimeException(StringUtils.format("Duplicate [id:{}] of static resource [resource:{}]", id, clazz.getSimpleName()));
|
|
||||||
}
|
|
||||||
// 添加资源
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var v = (V) value;
|
var v = (V) value;
|
||||||
dataMap.put(id, v);
|
dataMap.put((K) id, v);
|
||||||
// 添加索引
|
|
||||||
for (var def : indexDefMap.values()) {
|
|
||||||
// 使用field的名称作为索引的名称
|
|
||||||
var indexKey = def.getField().getName();
|
|
||||||
var indexValue = ReflectionUtils.getField(def.getField(), v);
|
|
||||||
if (def.isUnique()) {
|
|
||||||
var uniqueIndex = uniqueIndexMap.computeIfAbsent(indexKey, it -> new HashMap<>(values.size()));
|
|
||||||
if (uniqueIndex.put(indexValue, v) != null) {
|
|
||||||
throw new RuntimeException(StringUtils.format("Duplicate unique index [index:{}][value:{}] of static resource [class:{}]", indexKey, indexValue, clazz.getName()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var index = indexMap.computeIfAbsent(indexKey, it -> new HashMap<>(values.size()));
|
|
||||||
var list = index.computeIfAbsent(indexValue, it -> new ArrayList<V>());
|
|
||||||
list.add(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contain(K id) {
|
public boolean contain(K id) {
|
||||||
return dataMap.containsKey(id);
|
return contain((long) id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contain(int id) {
|
public boolean contain(int id) {
|
||||||
@SuppressWarnings("unchecked")
|
return contain((long) id);
|
||||||
var key = (K) Integer.valueOf(id);
|
|
||||||
return contain(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contain(long id) {
|
public boolean contain(long id) {
|
||||||
@SuppressWarnings("unchecked")
|
return dataMap.containsKey(id);
|
||||||
var key = (K) Long.valueOf(id);
|
|
||||||
return contain(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(K id) {
|
public V get(K id) {
|
||||||
|
return get((long) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(int id) {
|
||||||
|
return get((long) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(long id) {
|
||||||
V result = dataMap.get(id);
|
V result = dataMap.get(id);
|
||||||
AssertionUtils.notNull(result, "The static resource represented as [id:{}] in the static resource [resource:{}] does not exist", id, clazz.getSimpleName());
|
AssertionUtils.notNull(result, "The static resource represented as [id:{}] in the static resource [resource:{}] does not exist", id, clazz.getSimpleName());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(int id) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var key = (K) Integer.valueOf(id);
|
|
||||||
return get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(long id) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var key = (K) Long.valueOf(id);
|
|
||||||
return get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<V> getAll() {
|
public Collection<V> getAll() {
|
||||||
return dataMap.values();
|
return dataMap.values().stream().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<K, V> getData() {
|
public Map<K, V> getData() {
|
||||||
return Collections.unmodifiableMap(dataMap);
|
@SuppressWarnings("unchecked")
|
||||||
}
|
var map = (Map<K, V>) Collections.unmodifiableMap(dataMap);
|
||||||
|
return map;
|
||||||
@Override
|
|
||||||
public <INDEX> List<V> getIndexes(Func1<V, INDEX> func, INDEX index) {
|
|
||||||
String indexName = getMethodToField(func);
|
|
||||||
var indexValues = indexMap.get(indexName);
|
|
||||||
AssertionUtils.notNull(indexValues, "The index of [indexName:{}] does not exist in the static resource [resource:{}]", indexName, clazz.getSimpleName());
|
|
||||||
var values = indexValues.get(index);
|
|
||||||
if (CollectionUtils.isEmpty(values)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public <INDEX> V getUniqueIndex(Func1<V, INDEX> func, INDEX index) {
|
|
||||||
String uniqueIndexName = getMethodToField(func);
|
|
||||||
var indexValueMap = uniqueIndexMap.get(uniqueIndexName);
|
|
||||||
AssertionUtils.notNull(indexValueMap, "There is no a unique index for [uniqueIndexName:{}] in the static resource [resource:{}]", uniqueIndexName, clazz.getSimpleName());
|
|
||||||
var value = indexValueMap.get(index);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recycleStorage() {
|
|
||||||
recycle = true;
|
|
||||||
dataMap = null;
|
|
||||||
indexMap = null;
|
|
||||||
uniqueIndexMap = null;
|
|
||||||
idDef = null;
|
|
||||||
indexDefMap = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRecycle() {
|
|
||||||
return recycle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setRecycle(boolean recycle) {
|
|
||||||
this.recycle = recycle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IdDef getIdDef() {
|
|
||||||
return idDef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,69 +90,10 @@ public class StorageObject<K, V> implements IStorage<K, V> {
|
|||||||
return dataMap.size();
|
return dataMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private <INDEX> String getMethodToField(Func1<V, INDEX> func) {
|
@Override
|
||||||
var indexName = funcCaches.get(func);
|
public void recycleStorage() {
|
||||||
if (indexName != null) {
|
super.recycleStorage();
|
||||||
return indexName;
|
dataMap = null;
|
||||||
}
|
|
||||||
|
|
||||||
// 1. IDEA 调试模式下 lambda 表达式是一个代理
|
|
||||||
if (func instanceof Proxy) {
|
|
||||||
try {
|
|
||||||
var lambda = new IdeaProxyLambdaMeta((Proxy) func);
|
|
||||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 反射读取
|
|
||||||
if (indexName == null) {
|
|
||||||
try {
|
|
||||||
var method = func.getClass().getDeclaredMethod("writeReplace");
|
|
||||||
ReflectionUtils.makeAccessible(method);
|
|
||||||
var lambda = new ReflectLambdaMeta((java.lang.invoke.SerializedLambda) method.invoke(func));
|
|
||||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
|
||||||
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 反射失败使用序列化的方式读取
|
|
||||||
if (indexName == null) {
|
|
||||||
try {
|
|
||||||
var lambda = new ShadowLambdaMeta(SerializedLambda.extract(func));
|
|
||||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 通过将func带入到dataMap中求解,适合GraalVM环境中
|
|
||||||
if (indexName == null) {
|
|
||||||
try {
|
|
||||||
var fields = clazz.getDeclaredFields();
|
|
||||||
Arrays.stream(fields).forEach(ReflectionUtils::makeAccessible);
|
|
||||||
for (var value : dataMap.values()) {
|
|
||||||
var r = func.apply(value);
|
|
||||||
var valueFields = Arrays.stream(fields)
|
|
||||||
.map(it -> ReflectionUtils.getField(it, value))
|
|
||||||
.filter(it -> it.equals(r) && it.getClass() == r.getClass())
|
|
||||||
.toList();
|
|
||||||
// 如果只有一个能匹配到func的返回值则就是这个方法
|
|
||||||
if (valueFields.size() == 1) {
|
|
||||||
for (var field : fields) {
|
|
||||||
if (!ReflectionUtils.getField(field, value).equals(r)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
indexName = field.getName();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
funcCaches.put(func, indexName);
|
|
||||||
return indexName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user