Merge pull request #71 from veione/feat_storage

perf[storage]:优化配置表模块
This commit is contained in:
godotg
2023-09-13 20:52:29 +08:00
committed by GitHub
25 changed files with 650 additions and 58 deletions
@@ -15,6 +15,7 @@ package com.zfoo.protocol.util;
import com.zfoo.protocol.collection.ArrayUtils;
import com.zfoo.protocol.exception.RunException;
import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -24,6 +25,7 @@ import java.net.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
/**
* @author godotg
@@ -39,6 +41,21 @@ public abstract class ClassUtils {
public final static String JAR_PROTOCOL = "jar";
public final static String JAR_URL_SEPARATOR = "!/";
private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");
private static final Pattern IS_PATTERN = Pattern.compile("^is[A-Z].*");
private static ClassLoader systemClassLoader;
static {
try {
systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {
// AccessControlException on Google App Engine
}
}
private ClassUtils() {
}
public static Class<?> forName(String className) {
@@ -487,4 +504,55 @@ public abstract class ClassUtils {
|| Boolean.class.isAssignableFrom(clazz)
|| Character.class.isAssignableFrom(clazz);
}
/**
* @param name
* @param classLoader
* @return
* @since 3.4.3
*/
public static Class<?> toClassConfident(String name, ClassLoader classLoader) {
try {
return loadClass(name, getClassLoaders(classLoader));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
private static Class<?> loadClass(String className, ClassLoader[] classLoaders) throws ClassNotFoundException {
for (ClassLoader classLoader : classLoaders) {
if (classLoader != null) {
try {
return Class.forName(className, true, classLoader);
} catch (ClassNotFoundException e) {
// ignore
}
}
}
throw new ClassNotFoundException("Cannot find class: " + className);
}
private static ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
Thread.currentThread().getContextClassLoader(),
ClassUtils.class.getClassLoader(),
systemClassLoader};
}
/**
* 方法名转换为字段名
*
* @param methodName 方法名
* @return
*/
public static String getFieldName(String methodName) {
// 对于非标准变量生成的Get方法这里可以直接抛出异常,或者打印异常日志
if (GET_PATTERN.matcher(methodName).matches()) {
methodName = methodName.substring(3);
} else if (IS_PATTERN.matcher(methodName).matches()) {
methodName = methodName.substring(2);
}
return Introspector.decapitalize(methodName);
}
}
@@ -373,5 +373,28 @@ public abstract class ReflectionUtils {
}
}
public static <T> Constructor<T> getConstructor(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
throw new RunException("default constructor:[{}] not exists in class:[{}]", clazz.getCanonicalName());
}
}
public static <T> Constructor<T> getConstructor(Class<T> clazz, Class[] params) {
try {
return clazz.getConstructor(params);
} catch (NoSuchMethodException e) {
throw new RunException("constructor:[{}] has no setMethod in class:[{}]", params.length, clazz.getCanonicalName());
}
}
public static <T> T newInstance(Constructor<T> constructor, Object[] params) {
try {
return constructor.newInstance(params);
} catch (Exception e) {
throw new RunException("[{}]无法被实例化", constructor);
}
}
}
+9
View File
@@ -31,6 +31,9 @@
<!-- Office document parsing(office文档解析包) -->
<poi.version>5.2.3</poi.version>
<csv.version>1.10.0</csv.version>
<guava.version>32.1.2-jre</guava.version>
<java.version>17</java.version>
<file.encoding>UTF-8</file.encoding>
<!-- maven core plugin(maven核心插件) -->
<maven-clean-plugin.version>3.3.1</maven-clean-plugin.version>
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
@@ -109,6 +112,12 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>
@@ -15,6 +15,7 @@ package com.zfoo.storage;
import com.zfoo.scheduler.util.StopWatch;
import com.zfoo.storage.manager.IStorageManager;
import com.zfoo.storage.util.support.SerializableFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
@@ -24,6 +25,8 @@ import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import java.util.List;
/**
* @author godotg
*/
@@ -49,6 +52,22 @@ public class StorageContext implements ApplicationListener<ApplicationContextEve
return instance.storageManager;
}
public static <V, K> V get(Class<V> clazz, K keyId) {
return instance.storageManager.getStorage(clazz).get(keyId);
}
public static <V> List<V> getList(Class<V> clazz) {
return instance.storageManager.getStorage(clazz).getList();
}
public static <K, V> List<V> getIndexes(Class<V> clazz, SerializableFunction<V, ?> function, K indexId) {
return instance.storageManager.getStorage(clazz).getIndexes(function, indexId);
}
public static <K, V> V getUniqueIndex(Class<V> clazz, SerializableFunction<V, ?> function, K indexId) {
return instance.storageManager.getStorage(clazz).getUniqueIndex(function, indexId);
}
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
@@ -25,6 +25,7 @@ import org.springframework.core.convert.TypeDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
@@ -68,20 +69,45 @@ public class ResourceInterpreter {
var cellFieldMap = getCellFieldMap(resource, clazz);
var fieldInfos = getFieldInfos(cellFieldMap, clazz);
var iterator = resource.getRows().iterator();
// 从ROW_SERVER这行开始读取数据
while (iterator.hasNext()) {
var columns = iterator.next();
var instance = ReflectionUtils.newInstance(clazz);
if (clazz.isRecord()) {
Class[] constructParams = fieldInfos.stream().map(p -> p.field.getType()).toList().toArray(new Class[]{});
Constructor<T> constructor = ReflectionUtils.getConstructor(clazz, constructParams);
for (var fieldInfo : fieldInfos) {
var content = columns.get(fieldInfo.index);
if (StringUtils.isNotEmpty(content) || fieldInfo.field.getType() == String.class) {
inject(instance, fieldInfo.field, content);
var iterator = resource.getRows().iterator();
// 从ROW_SERVER这行开始读取数据
while (iterator.hasNext()) {
int index = 0;
var columns = iterator.next();
var params = new Object[fieldInfos.size()];
for (var fieldInfo : fieldInfos) {
var content = columns.get(fieldInfo.index);
if (StringUtils.isNotEmpty(content) || fieldInfo.field.getType() == String.class) {
var targetType = new TypeDescriptor(fieldInfo.field);
var value = conversionServiceFactoryBean.getObject().convert(content, TYPE_DESCRIPTOR, targetType);
params[index++] = value;
}
}
var instance = ReflectionUtils.newInstance(constructor, params);
result.add(instance);
}
} else {
var iterator = resource.getRows().iterator();
// 从ROW_SERVER这行开始读取数据
while (iterator.hasNext()) {
var columns = iterator.next();
var instance = ReflectionUtils.newInstance(clazz);
for (var fieldInfo : fieldInfos) {
var content = columns.get(fieldInfo.index);
if (StringUtils.isNotEmpty(content) || fieldInfo.field.getType() == String.class) {
inject(instance, fieldInfo.field, content);
}
}
result.add(instance);
}
result.add(instance);
}
return result;
}
@@ -38,7 +38,7 @@ public interface IStorageManager {
*/
void initAfter();
IStorage<?, ?> getStorage(Class<?> clazz);
<K, V, T extends IStorage<K, V>> T getStorage(Class<V> clazz);
Map<Class<?>, IStorage<?, ?>> storageMap();
@@ -18,6 +18,7 @@ import io.netty.util.collection.IntObjectHashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author godotg
@@ -85,7 +86,7 @@ public class StorageInt<K, V> extends StorageObject<K, V> {
@Override
public Collection<V> getAll() {
return dataMap.values();
return dataMap.values().stream().collect(Collectors.toUnmodifiableList());
}
@Override
@@ -18,6 +18,7 @@ import io.netty.util.collection.LongObjectHashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author godotg
@@ -85,7 +86,7 @@ public class StorageLong<K, V> extends StorageObject<K, V> {
@Override
public Collection<V> getAll() {
return dataMap.values();
return dataMap.values().stream().collect(Collectors.toUnmodifiableList());
}
@Override
@@ -26,6 +26,7 @@ import com.zfoo.storage.config.StorageConfig;
import com.zfoo.storage.interpreter.data.StorageEnum;
import com.zfoo.storage.model.IStorage;
import com.zfoo.storage.model.StorageDefinition;
import com.zfoo.storage.util.support.SerializableFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
@@ -190,8 +191,23 @@ public class StorageManager implements IStorageManager {
}
}
public <T> List<T> getList(Class<T> clazz) {
IStorage<?, ?> storage = getStorage(clazz);
return (List<T>) storage.getAll();
}
public <T, K> List<T> getIndexes(Class<T> clazz, SerializableFunction<T, ?> function, K indexId) {
var storage = getStorage(clazz);
return storage.getIndexes(function, indexId);
}
public <T, UQ> T get(Class<T> clazz, UQ uniqueId) {
IStorage<UQ, T> storage = getStorage(clazz);
return storage.get(uniqueId);
}
@Override
public IStorage<?, ?> getStorage(Class<?> clazz) {
public <K, V, T extends IStorage<K, V>> T getStorage(Class<V> clazz) {
var storage = storageMap.get(clazz);
if (storage == null) {
throw new RunException("There is no [{}] defined Storage and unable to get it", clazz.getCanonicalName());
@@ -200,7 +216,7 @@ public class StorageManager implements IStorageManager {
// Storage没有使用,为了节省内存提前释放了它;只有使用ResInjection注解的Storage才能被动态获取或者关闭配置recycle属性
logger.warn("Storage [{}] is not used, it was freed to save memory; use @ResInjection or turn off recycle configuration", clazz.getCanonicalName());
}
return storage;
return (T) storage;
}
@Override
@@ -12,8 +12,10 @@
package com.zfoo.storage.manager;
import com.google.common.collect.ImmutableMap;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.util.AssertionUtils;
import com.zfoo.protocol.util.ClassUtils;
import com.zfoo.protocol.util.IOUtils;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
@@ -21,26 +23,33 @@ 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.LambdaUtils;
import com.zfoo.storage.util.support.SerializableFunction;
import org.springframework.lang.Nullable;
import java.io.InputStream;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author godotg
*/
public class StorageObject<K, V> implements IStorage<K, V> {
private Map<K, V> dataMap = new HashMap<>();
private ImmutableMap<K, V> dataMap;
// 非唯一索引
protected Map<String, Map<Object, List<V>>> indexMap = new HashMap<>();
protected ImmutableMap<String, Map<Object, List<V>>> indexMap;
// 唯一索引
protected Map<String, Map<Object, V>> uniqueIndexMap = new HashMap<>();
protected ImmutableMap<String, Map<Object, V>> uniqueIndexMap;
protected Class<?> clazz;
protected IdDef idDef;
protected Map<String, IndexDef> indexDefMap;
// 当前配置表是否在当前项目中使用,没有被使用的会清data数据,以达到节省内存的目的
protected ImmutableMap<String, IndexDef> indexDefMap;
// 当前配置表是否在当前项目中使用,没有被使用的会清data数据,以达到节省内存的目的
protected boolean recycle = true;
@@ -50,11 +59,9 @@ public class StorageObject<K, V> implements IStorage<K, V> {
storage.clazz = resourceClazz;
var idDef = IdDef.valueOf(resourceClazz);
storage.idDef = idDef;
storage.indexDefMap = IndexDef.createResourceIndexes(resourceClazz);
storage.indexDefMap = ImmutableMap.copyOf(IndexDef.createResourceIndexes(resourceClazz));
var list = ResourceInterpreter.read(inputStream, resourceClazz, suffix);
for (var object : list) {
storage.put(object);
}
storage.append(list);
var idType = idDef.getField().getType();
if (idType == int.class || idType == Integer.class) {
return new StorageInt<>(storage);
@@ -135,6 +142,12 @@ public class StorageObject<K, V> implements IStorage<K, V> {
return dataMap.values();
}
@Override
public List<V> getList() {
Collection<V> all = getAll();
return all.stream().collect(Collectors.toUnmodifiableList());
}
@Override
public Map<K, V> getData() {
return Collections.unmodifiableMap(dataMap);
@@ -146,7 +159,8 @@ public class StorageObject<K, V> implements IStorage<K, V> {
}
@Override
public List<V> getIndex(String indexName, Object key) {
public <K> List<V> getIndexes(SerializableFunction<V, ?> indexFunction, K key) {
String indexName = ClassUtils.getFieldName(LambdaUtils.extract(indexFunction).getImplMethodName());
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(key);
@@ -158,11 +172,12 @@ public class StorageObject<K, V> implements IStorage<K, V> {
@Nullable
@Override
public V getUniqueIndex(String uniqueIndexName, Object key) {
public <K, V> V getUniqueIndex(SerializableFunction<V, ?> uniqueIndexFunction, K key) {
String uniqueIndexName = ClassUtils.getFieldName(LambdaUtils.extract(uniqueIndexFunction).getImplMethodName());
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(key);
return value;
return (V) value;
}
@Override
@@ -170,7 +185,45 @@ public class StorageObject<K, V> implements IStorage<K, V> {
return dataMap.size();
}
public V put(Object value) {
private void append(List<?> values) {
ImmutableMap.Builder<K, V> dataMapBuilder = ImmutableMap.builder();
Map<String, Map<Object, V>> uniqueIndexMap = new HashMap<>(32);
Map<String, Map<Object, List<V>>> indexMap = new HashMap<>(32);
for (var value : values) {
var key = (K) ReflectionUtils.getField(idDef.getField(), value);
if (key == null) {
throw new RuntimeException("There is an item with an unconfigured id in the static resource");
}
// 添加资源
var v = (V) value;
dataMapBuilder.put(key, v);
// 添加索引
for (var def : indexDefMap.values()) {
// 使用field的名称作为索引的名称
var indexKey = def.getField().getName();
var indexValue = ReflectionUtils.getField(def.getField(), v);
if (def.isUnique()) {// 唯一索引
var index = uniqueIndexMap.computeIfAbsent(indexKey, k -> new HashMap<>(8));
if (index.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, k -> new HashMap<>(12));
var list = index.computeIfAbsent(indexValue, k -> new ArrayList<>());
list.add(v);
}
}
}
this.uniqueIndexMap = ImmutableMap.copyOf(uniqueIndexMap);
this.indexMap = ImmutableMap.copyOf(indexMap);
this.dataMap = dataMapBuilder.build();
}
@Deprecated
protected V put(Object value) {
@SuppressWarnings("unchecked")
var key = (K) ReflectionUtils.getField(idDef.getField(), value);
@@ -12,6 +12,7 @@
package com.zfoo.storage.model;
import com.zfoo.storage.util.support.SerializableFunction;
import org.springframework.lang.Nullable;
import java.util.Collection;
@@ -43,18 +44,17 @@ public interface IStorage<K, V> {
Collection<V> getAll();
List<V> getList();
Map<K, V> getData();
IdDef getIdDef();
List<V> getIndex(String indexName, Object key);
@Nullable
<K> List<V> getIndexes(SerializableFunction<V, ?> function, K key);
@Nullable
V getUniqueIndex(String uniqueIndexName, Object key);
<K, V> V getUniqueIndex(SerializableFunction<V, ?> function, K key);
int size();
V put(Object value);
}
@@ -0,0 +1,55 @@
package com.zfoo.storage.util;
import com.zfoo.storage.util.support.IdeaProxyLambdaMeta;
import com.zfoo.storage.util.support.LambdaMeta;
import com.zfoo.storage.util.support.ReflectLambdaMeta;
import com.zfoo.storage.util.support.SerializableFunction;
import com.zfoo.storage.util.support.SerializedLambda;
import com.zfoo.storage.util.support.ShadowLambdaMeta;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessController;
/**
* @author veione
* @version 1.0
* @date 2023/9/12
*/
public final class LambdaUtils {
/**
* 该缓存可能会在任意不定的时间被清除
*
* @param func 需要解析的 lambda 对象
* @param <T> 类型,被调用的 Function 对象的目标类型
* @return 返回解析后的结果
*/
public static <T> LambdaMeta extract(SerializableFunction<T, ?> func) {
// 1. IDEA 调试模式下 lambda 表达式是一个代理
if (func instanceof Proxy) {
return new IdeaProxyLambdaMeta((Proxy) func);
}
// 2. 反射读取
try {
Method method = func.getClass().getDeclaredMethod("writeReplace");
return new ReflectLambdaMeta((SerializedLambda) setAccessible(method).invoke(func), func.getClass().getClassLoader());
} catch (Throwable e) {
// 3. 反射失败使用序列化的方式读取
return new ShadowLambdaMeta(SerializedLambda.extract(func));
}
}
/**
* 设置可访问对象的可访问权限为 true
*
* @param object 可访问的对象
* @param <T> 类型
* @return 返回设置后的对象
*/
public static <T extends AccessibleObject> T setAccessible(T object) {
return AccessController.doPrivileged(new SetAccessibleAction<>(object));
}
}
@@ -0,0 +1,22 @@
package com.zfoo.storage.util;
import java.lang.reflect.AccessibleObject;
import java.security.PrivilegedAction;
/**
* Create by hcl at 2021/5/14
*/
public class SetAccessibleAction<T extends AccessibleObject> implements PrivilegedAction<T> {
private final T obj;
public SetAccessibleAction(T obj) {
this.obj = obj;
}
@Override
public T run() {
obj.setAccessible(true);
return obj;
}
}
@@ -0,0 +1,40 @@
package com.zfoo.storage.util.support;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Executable;
import java.lang.reflect.Proxy;
/**
* 在 IDEA 的 Evaluate 中执行的 Lambda 表达式元数据需要使用该类处理元数据
* <p>
* Create by hcl at 2021/5/17
*/
public class IdeaProxyLambdaMeta implements LambdaMeta {
private final Class<?> clazz;
private final String name;
public IdeaProxyLambdaMeta(Proxy func) {
MethodHandle dmh = MethodHandleProxies.wrapperInstanceTarget(func);
Executable executable = MethodHandles.reflectAs(Executable.class, dmh);
clazz = executable.getDeclaringClass();
name = executable.getName();
}
@Override
public String getImplMethodName() {
return name;
}
@Override
public Class<?> getInstantiatedClass() {
return clazz;
}
@Override
public String toString() {
return clazz.getSimpleName() + "::" + name;
}
}
@@ -0,0 +1,24 @@
package com.zfoo.storage.util.support;
/**
* Lambda 信息
* <p>
* Created by hcl at 2021/5/14
*/
public interface LambdaMeta {
/**
* 获取 lambda 表达式实现方法的名称
*
* @return lambda 表达式对应的实现方法名称
*/
String getImplMethodName();
/**
* 实例化该方法的类
*
* @return 返回对应的类名称
*/
Class<?> getInstantiatedClass();
}
@@ -0,0 +1,30 @@
package com.zfoo.storage.util.support;
import com.zfoo.protocol.util.ClassUtils;
import com.zfoo.protocol.util.StringUtils;
/**
* Created by hcl at 2021/5/14
*/
public class ReflectLambdaMeta implements LambdaMeta {
private final SerializedLambda lambda;
private final ClassLoader classLoader;
public ReflectLambdaMeta(SerializedLambda lambda, ClassLoader classLoader) {
this.lambda = lambda;
this.classLoader = classLoader;
}
@Override
public String getImplMethodName() {
return lambda.getImplMethodName();
}
@Override
public Class<?> getInstantiatedClass() {
String instantiatedMethodType = lambda.getInstantiatedMethodType();
String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringUtils.SEMICOLON)).replace(StringUtils.SLASH, StringUtils.PERIOD);
return ClassUtils.toClassConfident(instantiatedType, this.classLoader);
}
}
@@ -0,0 +1,13 @@
package com.zfoo.storage.util.support;
import java.io.Serializable;
import java.util.function.Function;
/**
* 支持序列化的 Function
*
* @author veione
*/
@FunctionalInterface
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
}
@@ -0,0 +1,61 @@
package com.zfoo.storage.util.support;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
/**
* 当前类是 {@link java.lang.invoke.SerializedLambda } 的一个镜像
* <p>
* Create by hcl at 2020/7/17
*/
public class SerializedLambda implements Serializable {
private static final long serialVersionUID = 8025925345765570181L;
private Class<?> capturingClass;
private String functionalInterfaceClass;
private String functionalInterfaceMethodName;
private String functionalInterfaceMethodSignature;
private String implClass;
private String implMethodName;
private String implMethodSignature;
private int implMethodKind;
private String instantiatedMethodType;
private Object[] capturedArgs;
public static SerializedLambda extract(Serializable serializable) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(serializable);
oos.flush();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
Class<?> clazz = super.resolveClass(desc);
return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
}
}) {
return (SerializedLambda) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public String getInstantiatedMethodType() {
return instantiatedMethodType;
}
public Class<?> getCapturingClass() {
return capturingClass;
}
public String getImplMethodName() {
return implMethodName;
}
}
@@ -0,0 +1,30 @@
package com.zfoo.storage.util.support;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.protocol.util.ClassUtils;
/**
* 基于 {@link SerializedLambda} 创建的元信息
* <p>
* Create by hcl at 2021/7/7
*/
public class ShadowLambdaMeta implements LambdaMeta {
private final SerializedLambda lambda;
public ShadowLambdaMeta(SerializedLambda lambda) {
this.lambda = lambda;
}
@Override
public String getImplMethodName() {
return lambda.getImplMethodName();
}
@Override
public Class<?> getInstantiatedClass() {
String instantiatedMethodType = lambda.getInstantiatedMethodType();
String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringUtils.SEMICOLON)).replace(StringUtils.SLASH, StringUtils.PERIOD);
return ClassUtils.toClassConfident(instantiatedType, lambda.getCapturingClass().getClassLoader());
}
}
@@ -16,12 +16,16 @@ package com.zfoo.storage;
import com.zfoo.protocol.util.AssertionUtils;
import com.zfoo.protocol.util.JsonUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.manager.StorageManager;
import com.zfoo.storage.model.IStorage;
import com.zfoo.storage.resource.StudentCsvResource;
import com.zfoo.storage.resource.StudentResource;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
import java.util.Map;
/**
@@ -43,6 +47,19 @@ public class ApplicationTest {
// Excel的映射内容需要在被Spring管理的bean的方法上加上@ResInjection注解,即可自动注入Excel对应的对象
// 参考StudentManager中的标准用法
//获取所有数据
List<StudentResource> list = StorageContext.getList(StudentResource.class);
//根据主键获取数据
StudentResource studentResource = StorageContext.get(StudentResource.class, 1001);
//获取名字索引列表
List<StudentResource> nameIndexList = StorageContext.getIndexes(StudentResource.class, StudentResource::getName, "james1");
//获取年龄索引列表
List<StudentResource> ageIndexList = StorageContext.getIndexes(StudentResource.class, StudentResource::getAge, 10);
//获取Storage
IStorage<Integer, StudentResource> storage = context.getBean(StorageManager.class).getStorage(StudentResource.class);
var studentManager = context.getBean(StudentManager.class);
var studentResources = studentManager.studentResources;
var studentCsvResources = studentManager.studentCsvResources;
@@ -59,11 +76,11 @@ public class ApplicationTest {
System.out.println(StringUtils.MULTIPLE_HYPHENS);
// 通过索引找对应的行
var valuesByIndex = studentResources.getIndex("name", "james0");
var valuesByIndex = studentResources.getIndexes(StudentResource::getName, "james0");
logger.info(JsonUtils.object2String(valuesByIndex));
// 通过索引找对应的行
var csvValuesByIndex = studentCsvResources.getIndex("name", "james0");
var csvValuesByIndex = studentCsvResources.getIndexes(StudentCsvResource::getName, "james0");
logger.info(JsonUtils.object2String(csvValuesByIndex));
// Excel的映射内容需要在被Spring管理的bean的方法上加上@ResInjection注解,即可自动注入Excel对应的对象
@@ -0,0 +1,31 @@
package com.zfoo.storage;
import com.zfoo.storage.anno.AliasFieldName;
import com.zfoo.storage.anno.Id;
import com.zfoo.storage.anno.Index;
import com.zfoo.storage.anno.Storage;
import com.zfoo.storage.resource.User;
import java.util.List;
/**
* @author veione
* @version 1.0.0
*/
@Storage
public record StudentResource(
@Id
int id,
@Index(unique = true)
String idCard,
@Index
String name,
@AliasFieldName("年龄")
int age,
float score,
String[] courses,
User[] users,
List<User> userList,
User user
) {
}
@@ -12,19 +12,22 @@
package com.zfoo.storage.export;
import com.zfoo.protocol.ProtocolManager;
import com.zfoo.protocol.buffer.ByteBufUtils;
import com.zfoo.protocol.generate.GenerateOperation;
import com.zfoo.protocol.serializer.CodeLanguage;
import com.zfoo.protocol.util.ClassUtils;
import com.zfoo.protocol.util.FileUtils;
import com.zfoo.protocol.util.JsonUtils;
import com.zfoo.storage.anno.AliasFieldName;
import com.zfoo.storage.anno.Id;
import com.zfoo.storage.anno.Index;
import com.zfoo.storage.anno.Storage;
import com.zfoo.storage.config.StorageConfig;
import com.zfoo.storage.manager.StorageInt;
import com.zfoo.storage.manager.StorageManager;
import com.zfoo.storage.manager.StorageObject;
import com.zfoo.storage.util.ExportUtils;
import com.zfoo.storage.util.LambdaUtils;
import com.zfoo.storage.util.support.SerializableFunction;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.UnpooledHeapByteBuf;
import org.junit.Ignore;
@@ -33,6 +36,7 @@ import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
@@ -53,18 +57,22 @@ public class ExportBinaryTesting {
}
@Storage
public static class StudentResource {
public record StudentResource(
@Id
int id,
@Index(unique = true)
String idCard,
@Index
String name,
@Index
@AliasFieldName("年龄")
int age,
float score,
String[] courses,
User[] users,
User user
) {
@Id
public int id;
public String name;
@AliasFieldName("年龄")
public int age;
public float score;
public String[] courses;
public User[] users;
public User user;
}
@@ -87,23 +95,68 @@ public class ExportBinaryTesting {
storageManager.initBefore();
storageManager.initAfter();
// 生成协议
// 生成协议 TODO 协议
var protocols = new HashSet<Class<?>>();
protocols.add(ResourceData.class);
protocols.addAll(storageManager.storageMap().keySet());
var operation = new GenerateOperation();
operation.setProtocolPath("D:\\github\\godot-bird\\test\\storage\\protocol");
operation.getGenerateLanguages().add(CodeLanguage.GdScript);
ProtocolManager.initProtocolAuto(protocols, operation);
//ProtocolManager.initProtocolAuto(protocols, operation);
// 生成数据
var resourceData = ExportUtils.autoWrapData(ResourceData.class, storageManager.storageMap());
var buffer = new UnpooledHeapByteBuf(ByteBufAllocator.DEFAULT, 100, Integer.MAX_VALUE);
ProtocolManager.write(buffer, resourceData);
//ProtocolManager.write(buffer, resourceData);
var bytes = ByteBufUtils.readAllBytes(buffer);
FileUtils.writeInputStreamToFile(new File("D:/github/godot-bird/binary_data.cfg"), new ByteArrayInputStream(bytes));
@SuppressWarnings("unchecked")
var storage = (StorageObject<Integer, StudentResource>) storageManager.getStorage(StudentResource.class);
String methodName = LambdaUtils.extract(StudentResource::age).getImplMethodName();
String fieldName = ClassUtils.getFieldName(methodName);
System.out.println(methodName);
System.out.println(fieldName);
//获取storage对象
var storage = storageManager.getStorage(StudentResource.class);
//获取唯一索引的对象
StudentResource uniqueIndex = storage.getUniqueIndex(StudentResource::idCard, "110101200007281903");
System.out.println(JsonUtils.object2String(uniqueIndex));
//获取年龄索引对象列表
List<StudentResource> ageIndexList = storage.getIndexes(StudentResource::age, 10);
for (StudentResource resource : ageIndexList) {
System.out.println(JsonUtils.object2String(resource));
}
//获取名称索引对象列表
List<StudentResource> nameIndexList = storage.getIndexes(StudentResource::name, "james1");
for (StudentResource resource : nameIndexList) {
System.out.println(JsonUtils.object2String(resource));
}
//获取所有列表对象
List<StudentResource> list = storage.getList();
for (StudentResource resource : list) {
System.out.println(JsonUtils.object2String(resource));
}
//获取泛型的storage对象
StorageInt<Integer, StudentResource> genericStorage = storageManager.getStorage(StudentResource.class);
//获取配置表的所有数据
List<StudentResource> all = storageManager.getList(StudentResource.class);
for (StudentResource resource : all) {
System.out.println(JsonUtils.object2String(resource));
}
//获取配置表索引的数据
List<StudentResource> ageIndexes = storageManager.getIndexes(StudentResource.class, StudentResource::age, 10);
for (StudentResource resource : ageIndexes) {
System.out.println(JsonUtils.object2String(resource));
}
//获取配置表索引的数据
List<StudentResource> nameIndexes = storageManager.getIndexes(StudentResource.class, StudentResource::name, "james1");
for (StudentResource resource : nameIndexes) {
System.out.println(JsonUtils.object2String(resource));
}
//获取主键ID的唯一数据
StudentResource idResource = storageManager.get(StudentResource.class, 1001);
System.out.println(JsonUtils.object2String(idResource));
for (StudentResource resource : storage.getAll()) {
System.out.println(JsonUtils.object2String(resource));
}
@@ -34,7 +34,7 @@ public class StudentResource {
*/
@Index
private String name;
@Index
@AliasFieldName("年龄")
private int age;
private float score;
+1 -1
View File
@@ -48,7 +48,7 @@
<storage:storage id="resourceManager">
<!-- package以及location属性都支持同时指定多个路径,路径分隔符为, ;或者空格-->
<storage:scan package="com.zfoo.**.resource"/>
<storage:scan package="com.zfoo.**.resource" recycle="false"/>
<!-- 如果是类路径一classpath开头,如果是其它目录则以file开头,-->
<storage:resource location="classpath:/,classpath:/excel"/>
<!-- <storage:resource location="file:C:\Users\godotg\Desktop\excel"/>-->