mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-30 06:22:15 +00:00
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"/>-->
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user