mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-08-18 06:27:01 +00:00
ref[storage]: refactor lambda of storage
This commit is contained in:
@@ -3,6 +3,7 @@ package com.zfoo.protocol.util;
|
||||
import com.zfoo.protocol.exception.RunException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@@ -69,29 +70,54 @@ public abstract class FieldUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String methodToProperty(String name) {
|
||||
if (name.startsWith("is")) {
|
||||
name = name.substring(2);
|
||||
} else if (name.startsWith("get") || name.startsWith("set")) {
|
||||
name = name.substring(3);
|
||||
public static String getMethodToField(Class<?> clazz, String methodName) {
|
||||
try {
|
||||
// 查看clazz时候真的有methodName方法
|
||||
clazz.getDeclaredMethod(methodName);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RunException("clazz:[{}] has no getMethod:[{}]", clazz.getSimpleName(), methodName);
|
||||
}
|
||||
|
||||
if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
|
||||
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
|
||||
var fieldName = methodName;
|
||||
if (clazz.isRecord()) {
|
||||
try {
|
||||
clazz.getDeclaredField(fieldName);
|
||||
return fieldName;
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RunException("record clazz:[{}] has no field:[{}]", clazz.getSimpleName(), fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
// get method
|
||||
fieldName = StringUtils.substringAfterFirst(methodName, "get");
|
||||
try {
|
||||
clazz.getDeclaredField(fieldName);
|
||||
return fieldName;
|
||||
} catch (NoSuchFieldException e) {
|
||||
}
|
||||
|
||||
fieldName = StringUtils.uncapitalize(fieldName);
|
||||
try {
|
||||
clazz.getDeclaredField(fieldName);
|
||||
return fieldName;
|
||||
} catch (NoSuchFieldException e) {
|
||||
}
|
||||
|
||||
// is method
|
||||
fieldName = StringUtils.substringAfterFirst(methodName, "is");
|
||||
try {
|
||||
clazz.getDeclaredField(fieldName);
|
||||
return fieldName;
|
||||
} catch (NoSuchFieldException e) {
|
||||
}
|
||||
|
||||
fieldName = StringUtils.uncapitalize(fieldName);
|
||||
try {
|
||||
clazz.getDeclaredField(fieldName);
|
||||
return fieldName;
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RunException("clazz:[{}] has no field for getMethod:[{}]", clazz.getSimpleName(), methodName);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isProperty(String name) {
|
||||
return isGetter(name) || isSetter(name);
|
||||
}
|
||||
|
||||
public static boolean isGetter(String name) {
|
||||
return name.startsWith("get") && name.length() > 3 || name.startsWith("is") && name.length() > 2;
|
||||
}
|
||||
|
||||
public static boolean isSetter(String name) {
|
||||
return name.startsWith("set") && name.length() > 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,25 +13,28 @@
|
||||
package com.zfoo.storage.manager;
|
||||
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
import com.zfoo.protocol.util.AssertionUtils;
|
||||
import com.zfoo.protocol.util.IOUtils;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.protocol.util.*;
|
||||
import com.zfoo.storage.interpreter.ResourceInterpreter;
|
||||
import com.zfoo.storage.model.IStorage;
|
||||
import com.zfoo.storage.model.IdDef;
|
||||
import com.zfoo.storage.model.IndexDef;
|
||||
import com.zfoo.storage.util.LambdaUtils;
|
||||
import com.zfoo.storage.util.function.Func1;
|
||||
import com.zfoo.storage.util.lambda.*;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
*/
|
||||
public class StorageObject<K, V> implements IStorage<K, V> {
|
||||
// all storage data
|
||||
private Map<K, V> dataMap;
|
||||
// 非唯一索引
|
||||
protected Map<String, Map<Object, List<V>>> indexMap = new HashMap<>();
|
||||
@@ -41,9 +44,11 @@ public class StorageObject<K, V> implements IStorage<K, V> {
|
||||
protected Class<?> clazz;
|
||||
protected IdDef idDef;
|
||||
protected Map<String, IndexDef> indexDefMap;
|
||||
// 当前配置表是否在当前项目中使用,没有被使用的会清除data数据,以达到节省内存的目的
|
||||
// EN: unused configuration tables will clear data to save memory.
|
||||
// CN: 没有被使用的配置表会清除data数据,以达到节省内存的目的
|
||||
protected boolean recycle = true;
|
||||
|
||||
private ConcurrentReferenceHashMap<Func1<V, ?>, String> funcCaches = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
public static StorageObject<?, ?> parse(InputStream inputStream, Class<?> resourceClazz, String suffix) {
|
||||
var idDef = IdDef.valueOf(resourceClazz);
|
||||
@@ -162,7 +167,7 @@ public class StorageObject<K, V> implements IStorage<K, V> {
|
||||
|
||||
@Override
|
||||
public <INDEX> List<V> getIndexes(Func1<V, INDEX> func, INDEX index) {
|
||||
String indexName = LambdaUtils.getFieldName(func);
|
||||
String indexName = getMethodToField(func);
|
||||
var indexValues = indexMap.get(indexName);
|
||||
AssertionUtils.notNull(indexValues, "The index of [indexName:{}] does not exist in the static resource [resource:{}]", indexName, clazz.getSimpleName());
|
||||
var values = indexValues.get(index);
|
||||
@@ -175,7 +180,7 @@ public class StorageObject<K, V> implements IStorage<K, V> {
|
||||
@Nullable
|
||||
@Override
|
||||
public <INDEX> V getUniqueIndex(Func1<V, INDEX> func, INDEX index) {
|
||||
String uniqueIndexName = LambdaUtils.getFieldName(func);
|
||||
String uniqueIndexName = getMethodToField(func);
|
||||
var indexValueMap = uniqueIndexMap.get(uniqueIndexName);
|
||||
AssertionUtils.notNull(indexValueMap, "There is no a unique index for [uniqueIndexName:{}] in the static resource [resource:{}]", uniqueIndexName, clazz.getSimpleName());
|
||||
var value = indexValueMap.get(index);
|
||||
@@ -212,4 +217,69 @@ public class StorageObject<K, V> implements IStorage<K, V> {
|
||||
return dataMap.size();
|
||||
}
|
||||
|
||||
private <INDEX> String getMethodToField(Func1<V, INDEX> func) {
|
||||
var indexName = funcCaches.get(func);
|
||||
if (indexName != null) {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
// 1. IDEA 调试模式下 lambda 表达式是一个代理
|
||||
if (func instanceof Proxy) {
|
||||
try {
|
||||
var lambda = new IdeaProxyLambdaMeta((Proxy) func);
|
||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 反射读取
|
||||
if (indexName == null) {
|
||||
try {
|
||||
var method = func.getClass().getDeclaredMethod("writeReplace");
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
var lambda = new ReflectLambdaMeta((java.lang.invoke.SerializedLambda) method.invoke(func));
|
||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 反射失败使用序列化的方式读取
|
||||
if (indexName == null) {
|
||||
try {
|
||||
var lambda = new ShadowLambdaMeta(SerializedLambda.extract(func));
|
||||
indexName = FieldUtils.getMethodToField(clazz, lambda.getImplMethodName());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 通过将func带入到dataMap中求解,适合GraalVM环境中
|
||||
if (indexName == null) {
|
||||
try {
|
||||
var fields = clazz.getDeclaredFields();
|
||||
Arrays.stream(fields).forEach(ReflectionUtils::makeAccessible);
|
||||
for (var value : dataMap.values()) {
|
||||
var r = func.call(value);
|
||||
var valueFields = Arrays.stream(fields)
|
||||
.map(it -> ReflectionUtils.getField(it, value))
|
||||
.filter(it -> it.equals(r) && it.getClass() == r.getClass())
|
||||
.toList();
|
||||
// 如果只有一个能匹配到func的返回值则就是这个方法
|
||||
if (valueFields.size() == 1) {
|
||||
for (var field : fields) {
|
||||
if (!ReflectionUtils.getField(field, value).equals(r)) {
|
||||
continue;
|
||||
}
|
||||
indexName = field.getName();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
funcCaches.put(func, indexName);
|
||||
return indexName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.zfoo.storage.util;
|
||||
|
||||
import com.zfoo.protocol.util.FieldUtils;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.storage.util.function.Func1;
|
||||
import com.zfoo.storage.util.lambda.*;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* @author veione
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class LambdaUtils {
|
||||
private static final ConcurrentReferenceHashMap<Class<?>, String> CACHE = new ConcurrentReferenceHashMap<>(64, 0.75F, 16, ConcurrentReferenceHashMap.ReferenceType.SOFT);
|
||||
|
||||
/**
|
||||
* 该缓存可能会在任意不定的时间被清除
|
||||
*
|
||||
* @param func 需要解析的 lambda 对象
|
||||
* @return 返回解析后的结果
|
||||
*/
|
||||
public static LambdaMeta extract(Serializable func) {
|
||||
// 1. IDEA 调试模式下 lambda 表达式是一个代理
|
||||
if (func instanceof Proxy) {
|
||||
return new IdeaProxyLambdaMeta((Proxy) func);
|
||||
}
|
||||
|
||||
// 2. 反射读取
|
||||
try {
|
||||
Class<? extends Serializable> clazz = func.getClass();
|
||||
Method method = clazz.getDeclaredMethod("writeReplace");
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return new ReflectLambdaMeta((java.lang.invoke.SerializedLambda) method.invoke(func));
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
|
||||
// 3. 反射失败使用序列化的方式读取
|
||||
return new ShadowLambdaMeta(SerializedLambda.extract(func));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取lambda表达式函数(方法)名称
|
||||
*
|
||||
* @param <T> Lambda类型
|
||||
* @param func 函数(无参方法)
|
||||
* @return 函数名称
|
||||
*/
|
||||
public static <T> String getMethodName(Func1<T, ?> func) {
|
||||
return CACHE.computeIfAbsent(func.getClass(), it -> extract(func).getImplMethodName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取lambda表达式函数(字段)名称
|
||||
*
|
||||
* @param <T> Lambda类型
|
||||
* @param func 函数
|
||||
* @return 字段名称
|
||||
*/
|
||||
public static <T> String getFieldName(Func1<T, ?> func) {
|
||||
return FieldUtils.methodToProperty(getMethodName(func));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@ 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.FieldUtils;
|
||||
import com.zfoo.protocol.util.FileUtils;
|
||||
import com.zfoo.protocol.util.JsonUtils;
|
||||
import com.zfoo.storage.anno.AliasFieldName;
|
||||
@@ -27,8 +26,6 @@ import com.zfoo.storage.config.StorageConfig;
|
||||
import com.zfoo.storage.manager.StorageInt;
|
||||
import com.zfoo.storage.manager.StorageManager;
|
||||
import com.zfoo.storage.util.ExportUtils;
|
||||
import com.zfoo.storage.util.LambdaUtils;
|
||||
import com.zfoo.storage.util.function.Func1;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.UnpooledHeapByteBuf;
|
||||
import org.junit.Ignore;
|
||||
@@ -112,13 +109,6 @@ public class ExportBinaryTesting {
|
||||
var bytes = ByteBufUtils.readAllBytes(buffer);
|
||||
FileUtils.writeInputStreamToFile(new File("D:/github/godot-bird/binary_data.cfg"), new ByteArrayInputStream(bytes));
|
||||
|
||||
Func1<StudentResource, Integer> ageFunc = StudentResource::age;
|
||||
String methodName = LambdaUtils.extract(ageFunc).getImplMethodName();
|
||||
String fieldName = FieldUtils.methodToProperty(methodName);
|
||||
System.out.println(methodName);
|
||||
System.out.println(fieldName);
|
||||
|
||||
|
||||
//获取storage对象
|
||||
var storage = storageManager.getStorage(StudentResource.class);
|
||||
//获取唯一索引的对象
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zfoo.storage.util;
|
||||
|
||||
import com.zfoo.protocol.util.FieldUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author veione
|
||||
*/
|
||||
public class FieldUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testPropertyName() {
|
||||
//前三种主要用于普通的POJO,第四种用于是record类型,方法名就是属性名
|
||||
String getName = FieldUtils.methodToProperty("getName");
|
||||
assertEquals(getName, "name");
|
||||
String setName = FieldUtils.methodToProperty("setName");
|
||||
assertEquals(setName, "name");
|
||||
String isName = FieldUtils.methodToProperty("isName");
|
||||
assertEquals(isName, "name");
|
||||
String name = FieldUtils.methodToProperty("name");
|
||||
assertEquals(name, "name");
|
||||
name = FieldUtils.methodToProperty("Name");
|
||||
assertEquals(name, "name");
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zfoo.storage.util;
|
||||
|
||||
import com.zfoo.storage.resource.StudentResource;
|
||||
import com.zfoo.storage.util.function.Func1;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author veione
|
||||
*/
|
||||
public class LambdaFunctionCacheTest {
|
||||
|
||||
@Test
|
||||
public void testFunctionCache() {
|
||||
Func1<StudentResource, Object> nameFunc = StudentResource::getName;
|
||||
System.out.println(LambdaUtils.getMethodName(nameFunc));
|
||||
System.out.println(LambdaUtils.getMethodName(nameFunc));
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
package com.zfoo.storage.util;
|
||||
|
||||
import com.zfoo.storage.resource.StudentResource;
|
||||
import com.zfoo.storage.resource.TeacherResource;
|
||||
import com.zfoo.storage.util.function.Func1;
|
||||
import com.zfoo.storage.util.lambda.LambdaMeta;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -24,6 +25,8 @@ import java.lang.reflect.Method;
|
||||
* @author veione
|
||||
*/
|
||||
public class LambdaFunctionTest {
|
||||
|
||||
|
||||
// https://blog.csdn.net/iteye_19045/article/details/119299015
|
||||
@Test
|
||||
public void testFuncSerialization() throws Exception {
|
||||
@@ -35,15 +38,20 @@ public class LambdaFunctionTest {
|
||||
Method writeReplace = clazz.getDeclaredMethod("writeReplace");
|
||||
System.out.println(writeReplace);
|
||||
|
||||
LambdaMeta meta = LambdaUtils.extract(func);
|
||||
System.out.println(meta);
|
||||
|
||||
// Function<StudentResource, String> func2 = StudentResource::name;
|
||||
// Method method = func2.getClass().getDeclaredMethod("writeReplace");
|
||||
// object = method.invoke(func2);
|
||||
// System.out.println(object);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFunctionCache() {
|
||||
Func1<StudentResource, Object> nameFunc1 = StudentResource::getName;
|
||||
Func1<StudentResource, Object> nameFunc2 = StudentResource::getName;
|
||||
Assert.assertNotEquals(nameFunc1, nameFunc2);
|
||||
}
|
||||
|
||||
private static Object getObject(Serializable func) throws Exception {
|
||||
Method method = func.getClass().getDeclaredMethod("writeReplace");
|
||||
Object object = method.invoke(func);
|
||||
|
||||
Reference in New Issue
Block a user