diff --git a/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java b/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java index e4ce8fa2..3a98a64e 100644 --- a/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java @@ -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); + } } diff --git a/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java b/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java index ab9ba5dc..fc628cca 100644 --- a/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java @@ -373,5 +373,28 @@ public abstract class ReflectionUtils { } } + public static Constructor getConstructor(Class clazz) { + try { + return clazz.getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new RunException("default constructor:[{}] not exists in class:[{}]", clazz.getCanonicalName()); + } + } + + public static Constructor getConstructor(Class 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 newInstance(Constructor constructor, Object[] params) { + try { + return constructor.newInstance(params); + } catch (Exception e) { + throw new RunException("[{}]无法被实例化", constructor); + } + } } diff --git a/storage/pom.xml b/storage/pom.xml index 045ab2dd..ab9ca1a8 100644 --- a/storage/pom.xml +++ b/storage/pom.xml @@ -31,6 +31,9 @@ 5.2.3 1.10.0 + 32.1.2-jre + 17 + UTF-8 3.3.1 3.3.1 @@ -109,6 +112,12 @@ ${junit.version} test + + + com.google.guava + guava + ${guava.version} + diff --git a/storage/src/main/java/com/zfoo/storage/StorageContext.java b/storage/src/main/java/com/zfoo/storage/StorageContext.java index 6a504b06..625d97dd 100644 --- a/storage/src/main/java/com/zfoo/storage/StorageContext.java +++ b/storage/src/main/java/com/zfoo/storage/StorageContext.java @@ -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 V get(Class clazz, K keyId) { + return instance.storageManager.getStorage(clazz).get(keyId); + } + + public static List getList(Class clazz) { + return instance.storageManager.getStorage(clazz).getList(); + } + + public static List getIndexes(Class clazz, SerializableFunction function, K indexId) { + return instance.storageManager.getStorage(clazz).getIndexes(function, indexId); + } + + public static V getUniqueIndex(Class clazz, SerializableFunction function, K indexId) { + return instance.storageManager.getStorage(clazz).getUniqueIndex(function, indexId); + } + @Override public void onApplicationEvent(ApplicationContextEvent event) { diff --git a/storage/src/main/java/com/zfoo/storage/interpreter/ResourceInterpreter.java b/storage/src/main/java/com/zfoo/storage/interpreter/ResourceInterpreter.java index c574f592..198cbc17 100644 --- a/storage/src/main/java/com/zfoo/storage/interpreter/ResourceInterpreter.java +++ b/storage/src/main/java/com/zfoo/storage/interpreter/ResourceInterpreter.java @@ -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 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; } diff --git a/storage/src/main/java/com/zfoo/storage/manager/IStorageManager.java b/storage/src/main/java/com/zfoo/storage/manager/IStorageManager.java index c77a1ad0..bb3d7e90 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/IStorageManager.java +++ b/storage/src/main/java/com/zfoo/storage/manager/IStorageManager.java @@ -38,7 +38,7 @@ public interface IStorageManager { */ void initAfter(); - IStorage getStorage(Class clazz); + > T getStorage(Class clazz); Map, IStorage> storageMap(); diff --git a/storage/src/main/java/com/zfoo/storage/manager/StorageInt.java b/storage/src/main/java/com/zfoo/storage/manager/StorageInt.java index 8ee4a438..e4f217a0 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/StorageInt.java +++ b/storage/src/main/java/com/zfoo/storage/manager/StorageInt.java @@ -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 extends StorageObject { @Override public Collection getAll() { - return dataMap.values(); + return dataMap.values().stream().collect(Collectors.toUnmodifiableList()); } @Override diff --git a/storage/src/main/java/com/zfoo/storage/manager/StorageLong.java b/storage/src/main/java/com/zfoo/storage/manager/StorageLong.java index 3c75fbcf..57af2ea8 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/StorageLong.java +++ b/storage/src/main/java/com/zfoo/storage/manager/StorageLong.java @@ -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 extends StorageObject { @Override public Collection getAll() { - return dataMap.values(); + return dataMap.values().stream().collect(Collectors.toUnmodifiableList()); } @Override diff --git a/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java b/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java index f8f54942..f97ea84c 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java +++ b/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java @@ -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 List getList(Class clazz) { + IStorage storage = getStorage(clazz); + return (List) storage.getAll(); + } + + public List getIndexes(Class clazz, SerializableFunction function, K indexId) { + var storage = getStorage(clazz); + return storage.getIndexes(function, indexId); + } + + public T get(Class clazz, UQ uniqueId) { + IStorage storage = getStorage(clazz); + return storage.get(uniqueId); + } + @Override - public IStorage getStorage(Class clazz) { + public > T getStorage(Class 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 diff --git a/storage/src/main/java/com/zfoo/storage/manager/StorageObject.java b/storage/src/main/java/com/zfoo/storage/manager/StorageObject.java index 3a51c284..a145df16 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/StorageObject.java +++ b/storage/src/main/java/com/zfoo/storage/manager/StorageObject.java @@ -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 implements IStorage { - - private Map dataMap = new HashMap<>(); + private ImmutableMap dataMap; // 非唯一索引 - protected Map>> indexMap = new HashMap<>(); + protected ImmutableMap>> indexMap; // 唯一索引 - protected Map> uniqueIndexMap = new HashMap<>(); + protected ImmutableMap> uniqueIndexMap; protected Class clazz; protected IdDef idDef; - protected Map indexDefMap; - // 当前配置表是否在当前项目中使用,没有被使用的会清楚data数据,以达到节省内存的目的 + protected ImmutableMap indexDefMap; + // 当前配置表是否在当前项目中使用,没有被使用的会清除data数据,以达到节省内存的目的 protected boolean recycle = true; @@ -50,11 +59,9 @@ public class StorageObject implements IStorage { 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 implements IStorage { return dataMap.values(); } + @Override + public List getList() { + Collection all = getAll(); + return all.stream().collect(Collectors.toUnmodifiableList()); + } + @Override public Map getData() { return Collections.unmodifiableMap(dataMap); @@ -146,7 +159,8 @@ public class StorageObject implements IStorage { } @Override - public List getIndex(String indexName, Object key) { + public List getIndexes(SerializableFunction 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 implements IStorage { @Nullable @Override - public V getUniqueIndex(String uniqueIndexName, Object key) { + public V getUniqueIndex(SerializableFunction 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 implements IStorage { return dataMap.size(); } - public V put(Object value) { + private void append(List values) { + ImmutableMap.Builder dataMapBuilder = ImmutableMap.builder(); + Map> uniqueIndexMap = new HashMap<>(32); + Map>> 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); diff --git a/storage/src/main/java/com/zfoo/storage/model/IStorage.java b/storage/src/main/java/com/zfoo/storage/model/IStorage.java index 8e79989b..c7a6c28e 100644 --- a/storage/src/main/java/com/zfoo/storage/model/IStorage.java +++ b/storage/src/main/java/com/zfoo/storage/model/IStorage.java @@ -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 { Collection getAll(); + List getList(); + Map getData(); IdDef getIdDef(); - List getIndex(String indexName, Object key); + @Nullable + List getIndexes(SerializableFunction function, K key); @Nullable - V getUniqueIndex(String uniqueIndexName, Object key); + V getUniqueIndex(SerializableFunction function, K key); int size(); - - V put(Object value); - - } diff --git a/storage/src/main/java/com/zfoo/storage/util/LambdaUtils.java b/storage/src/main/java/com/zfoo/storage/util/LambdaUtils.java new file mode 100644 index 00000000..8076da2b --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/LambdaUtils.java @@ -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 类型,被调用的 Function 对象的目标类型 + * @return 返回解析后的结果 + */ + public static LambdaMeta extract(SerializableFunction 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 类型 + * @return 返回设置后的对象 + */ + public static T setAccessible(T object) { + return AccessController.doPrivileged(new SetAccessibleAction<>(object)); + } +} diff --git a/storage/src/main/java/com/zfoo/storage/util/SetAccessibleAction.java b/storage/src/main/java/com/zfoo/storage/util/SetAccessibleAction.java new file mode 100644 index 00000000..c90fcc68 --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/SetAccessibleAction.java @@ -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 implements PrivilegedAction { + private final T obj; + + public SetAccessibleAction(T obj) { + this.obj = obj; + } + + @Override + public T run() { + obj.setAccessible(true); + return obj; + } + +} diff --git a/storage/src/main/java/com/zfoo/storage/util/support/IdeaProxyLambdaMeta.java b/storage/src/main/java/com/zfoo/storage/util/support/IdeaProxyLambdaMeta.java new file mode 100644 index 00000000..34ab4de4 --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/IdeaProxyLambdaMeta.java @@ -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 表达式元数据需要使用该类处理元数据 + *

+ * 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; + } + +} diff --git a/storage/src/main/java/com/zfoo/storage/util/support/LambdaMeta.java b/storage/src/main/java/com/zfoo/storage/util/support/LambdaMeta.java new file mode 100644 index 00000000..d7a8910f --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/LambdaMeta.java @@ -0,0 +1,24 @@ +package com.zfoo.storage.util.support; + +/** + * Lambda 信息 + *

+ * Created by hcl at 2021/5/14 + */ +public interface LambdaMeta { + + /** + * 获取 lambda 表达式实现方法的名称 + * + * @return lambda 表达式对应的实现方法名称 + */ + String getImplMethodName(); + + /** + * 实例化该方法的类 + * + * @return 返回对应的类名称 + */ + Class getInstantiatedClass(); + +} diff --git a/storage/src/main/java/com/zfoo/storage/util/support/ReflectLambdaMeta.java b/storage/src/main/java/com/zfoo/storage/util/support/ReflectLambdaMeta.java new file mode 100644 index 00000000..3ae8e60c --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/ReflectLambdaMeta.java @@ -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); + } +} \ No newline at end of file diff --git a/storage/src/main/java/com/zfoo/storage/util/support/SerializableFunction.java b/storage/src/main/java/com/zfoo/storage/util/support/SerializableFunction.java new file mode 100644 index 00000000..eac5f769 --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/SerializableFunction.java @@ -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 extends Function, Serializable { +} diff --git a/storage/src/main/java/com/zfoo/storage/util/support/SerializedLambda.java b/storage/src/main/java/com/zfoo/storage/util/support/SerializedLambda.java new file mode 100644 index 00000000..b64f150d --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/SerializedLambda.java @@ -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 } 的一个镜像 + *

+ * 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; + } +} diff --git a/storage/src/main/java/com/zfoo/storage/util/support/ShadowLambdaMeta.java b/storage/src/main/java/com/zfoo/storage/util/support/ShadowLambdaMeta.java new file mode 100644 index 00000000..7538c000 --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/util/support/ShadowLambdaMeta.java @@ -0,0 +1,30 @@ +package com.zfoo.storage.util.support; + +import com.zfoo.protocol.util.StringUtils; +import com.zfoo.protocol.util.ClassUtils; + +/** + * 基于 {@link SerializedLambda} 创建的元信息 + *

+ * 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()); + } + +} diff --git a/storage/src/test/java/com/zfoo/storage/ApplicationTest.java b/storage/src/test/java/com/zfoo/storage/ApplicationTest.java index 5b4096ef..9d493ad4 100644 --- a/storage/src/test/java/com/zfoo/storage/ApplicationTest.java +++ b/storage/src/test/java/com/zfoo/storage/ApplicationTest.java @@ -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 list = StorageContext.getList(StudentResource.class); + //根据主键获取数据 + StudentResource studentResource = StorageContext.get(StudentResource.class, 1001); + //获取名字索引列表 + List nameIndexList = StorageContext.getIndexes(StudentResource.class, StudentResource::getName, "james1"); + //获取年龄索引列表 + List ageIndexList = StorageContext.getIndexes(StudentResource.class, StudentResource::getAge, 10); + + //获取Storage + IStorage 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对应的对象 diff --git a/storage/src/test/java/com/zfoo/storage/StudentResource.java b/storage/src/test/java/com/zfoo/storage/StudentResource.java new file mode 100644 index 00000000..ddde5f62 --- /dev/null +++ b/storage/src/test/java/com/zfoo/storage/StudentResource.java @@ -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 userList, + User user +) { +} diff --git a/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java b/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java index 0c8c0a80..c3f558a8 100644 --- a/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java +++ b/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java @@ -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>(); 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) 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 ageIndexList = storage.getIndexes(StudentResource::age, 10); + for (StudentResource resource : ageIndexList) { + System.out.println(JsonUtils.object2String(resource)); + } + //获取名称索引对象列表 + List nameIndexList = storage.getIndexes(StudentResource::name, "james1"); + for (StudentResource resource : nameIndexList) { + System.out.println(JsonUtils.object2String(resource)); + } + //获取所有列表对象 + List list = storage.getList(); + for (StudentResource resource : list) { + System.out.println(JsonUtils.object2String(resource)); + } + //获取泛型的storage对象 + StorageInt genericStorage = storageManager.getStorage(StudentResource.class); + //获取配置表的所有数据 + List all = storageManager.getList(StudentResource.class); + for (StudentResource resource : all) { + System.out.println(JsonUtils.object2String(resource)); + } + //获取配置表索引的数据 + List ageIndexes = storageManager.getIndexes(StudentResource.class, StudentResource::age, 10); + for (StudentResource resource : ageIndexes) { + System.out.println(JsonUtils.object2String(resource)); + } + //获取配置表索引的数据 + List 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)); } diff --git a/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java b/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java index b09eba90..379793ab 100644 --- a/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java +++ b/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java @@ -34,7 +34,7 @@ public class StudentResource { */ @Index private String name; - + @Index @AliasFieldName("年龄") private int age; private float score; diff --git a/storage/src/test/resources/application.xml b/storage/src/test/resources/application.xml index 0353107a..8d411298 100644 --- a/storage/src/test/resources/application.xml +++ b/storage/src/test/resources/application.xml @@ -48,7 +48,7 @@ - + diff --git a/storage/src/test/resources/excel/StudentResource.xlsx b/storage/src/test/resources/excel/StudentResource.xlsx index eed0851e..8ac64b66 100644 Binary files a/storage/src/test/resources/excel/StudentResource.xlsx and b/storage/src/test/resources/excel/StudentResource.xlsx differ