mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-08-18 04:28:40 +00:00
perf[orm]: 循环对象校验和格式语法分析校验
This commit is contained in:
+2
-1
@@ -6,12 +6,13 @@
|
||||
|
||||
- POJO对象的属性必须提供get和set方法,否则无法映射
|
||||
- 不支持泛型
|
||||
- 不支持循环引用的对象
|
||||
- 如果不想映射某属性,直接加上transient关键字
|
||||
- 目前支持基本数据属性(byte,short,int,long,float,double,boolean),字符串String,List,Set集合属性的映射,不支持Map
|
||||
- 数据库主键能用整数尽量用整数,因为MongoDB默认的主键是一个字符串,比较占空间
|
||||
- 数据库使用自研的orm框架,比如一个实体类UserEntity,映射到数据库中的集合为user,首字母小写,去掉Entity
|
||||
- 基于 [caffeine](https://github.com/ben-manes/caffeine) 的高性能数据缓存
|
||||
- 语法校验,如对没有加上get和set的字段自动语法提示
|
||||
- 智能语法分析,错误的entity对象定义将无法启动程序并给出错误警告,
|
||||
|
||||
### Ⅲ. 使用方法
|
||||
|
||||
|
||||
@@ -20,18 +20,22 @@ import com.mongodb.client.*;
|
||||
import com.mongodb.client.model.IndexOptions;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import com.zfoo.orm.OrmContext;
|
||||
import com.zfoo.orm.model.anno.EntityCache;
|
||||
import com.zfoo.orm.model.anno.EntityCachesInjection;
|
||||
import com.zfoo.orm.model.anno.*;
|
||||
import com.zfoo.orm.model.cache.EntityCaches;
|
||||
import com.zfoo.orm.model.cache.IEntityCaches;
|
||||
import com.zfoo.orm.model.config.OrmConfig;
|
||||
import com.zfoo.orm.model.entity.IEntity;
|
||||
import com.zfoo.orm.model.vo.EntityDef;
|
||||
import com.zfoo.orm.model.vo.IndexDef;
|
||||
import com.zfoo.orm.model.vo.IndexTextDef;
|
||||
import com.zfoo.protocol.collection.ArrayUtils;
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
import com.zfoo.protocol.exception.RunException;
|
||||
import com.zfoo.protocol.util.AssertionUtils;
|
||||
import com.zfoo.protocol.util.JsonUtils;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.util.math.RandomUtils;
|
||||
import com.zfoo.util.net.HostAndPort;
|
||||
import org.bson.Document;
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
@@ -45,6 +49,7 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
@@ -77,7 +82,7 @@ public class OrmManager implements IOrmManager {
|
||||
|
||||
@Override
|
||||
public void initBefore() {
|
||||
var entityDefMap = scanEntity();
|
||||
var entityDefMap = scanEntityClass();
|
||||
|
||||
for (var entityDef : entityDefMap.values()) {
|
||||
var entityCaches = new EntityCaches(entityDef);
|
||||
@@ -238,28 +243,25 @@ public class OrmManager implements IOrmManager {
|
||||
return mongodbDatabase.getCollection(collection);
|
||||
}
|
||||
|
||||
private Map<Class<? extends IEntity<?>>, EntityDef> scanEntity() {
|
||||
private Map<Class<? extends IEntity<?>>, EntityDef> scanEntityClass() {
|
||||
var cacheDefMap = new HashMap<Class<? extends IEntity<?>>, EntityDef>();
|
||||
var entityPackage = ormConfig.getEntityPackage();
|
||||
var cacheStrategies = ormConfig.getCachesConfig().getCacheStrategies();
|
||||
var persisterStrategies = ormConfig.getPersistersConfig().getPersisterStrategies();
|
||||
|
||||
var locationSet = getEntityLocation(entityPackage);
|
||||
var locationSet = scanEntityCacheAnno(ormConfig.getEntityPackage());
|
||||
for (var location : locationSet) {
|
||||
Class<?> entityClazz;
|
||||
Class<? extends IEntity<?>> entityClazz;
|
||||
try {
|
||||
entityClazz = Class.forName(location);
|
||||
entityClazz = (Class<? extends IEntity<?>>) Class.forName(location);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(StringUtils.format("无法获取实体类[{}]", location));
|
||||
throw new RunException("无法获取实体类[{}]", location);
|
||||
}
|
||||
var cacheDef = EntityDef.valueOf(entityClazz, cacheStrategies, persisterStrategies);
|
||||
var previousCacheDef = cacheDefMap.putIfAbsent((Class<? extends IEntity<?>>) entityClazz, cacheDef);
|
||||
var cacheDef = parserEntityDef(entityClazz);
|
||||
var previousCacheDef = cacheDefMap.putIfAbsent(entityClazz, cacheDef);
|
||||
AssertionUtils.isNull(previousCacheDef, "缓存实体不能包含重复的[class:{}]", entityClazz.getSimpleName());
|
||||
}
|
||||
return cacheDefMap;
|
||||
}
|
||||
|
||||
private Set<String> getEntityLocation(String scanLocation) {
|
||||
private Set<String> scanEntityCacheAnno(String scanLocation) {
|
||||
var prefixPattern = "classpath*:";
|
||||
var suffixPattern = "**/*.class";
|
||||
|
||||
@@ -286,4 +288,207 @@ public class OrmManager implements IOrmManager {
|
||||
throw new RuntimeException("无法读取实体信息:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public EntityDef parserEntityDef(Class<? extends IEntity<?>> clazz) {
|
||||
analyze(clazz);
|
||||
|
||||
var cacheStrategies = ormConfig.getCachesConfig().getCacheStrategies();
|
||||
var persisterStrategies = ormConfig.getPersistersConfig().getPersisterStrategies();
|
||||
|
||||
var cache = clazz.getAnnotation(EntityCache.class);
|
||||
var cacheStrategyOptional = cacheStrategies.stream().filter(it -> it.getStrategy().equals(cache.cacheStrategy())).findFirst();
|
||||
AssertionUtils.isTrue(cacheStrategyOptional.isPresent(), "实体类Entity[{}]没有找到缓存策略[{}]", clazz.getSimpleName(), cache.cacheStrategy());
|
||||
|
||||
var cacheStrategy = cacheStrategyOptional.get();
|
||||
var cacheSize = cacheStrategy.getSize();
|
||||
var expireMillisecond = cacheStrategy.getExpireMillisecond();
|
||||
|
||||
var idField = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class)[0];
|
||||
ReflectionUtils.makeAccessible(idField);
|
||||
|
||||
var persister = cache.persister();
|
||||
var persisterStrategyOptional = persisterStrategies.stream().filter(it -> it.getStrategy().equals(persister.value())).findFirst();
|
||||
AssertionUtils.isTrue(persisterStrategyOptional.isPresent(), "实体类Entity[{}]没有找到持久化策略[{}]", clazz.getSimpleName(), persister);
|
||||
|
||||
var persisterStrategy = persisterStrategyOptional.get();
|
||||
var indexDefMap = new HashMap<String, IndexDef>();
|
||||
var fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Index.class);
|
||||
for (var field : fields) {
|
||||
var indexAnnotation = field.getAnnotation(Index.class);
|
||||
IndexDef indexDef = new IndexDef(field, indexAnnotation.ascending(), indexAnnotation.unique());
|
||||
indexDefMap.put(field.getName(), indexDef);
|
||||
}
|
||||
|
||||
var indexTextDefMap = new HashMap<String, IndexTextDef>();
|
||||
fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, IndexText.class);
|
||||
for (var field : fields) {
|
||||
IndexTextDef indexTextDef = new IndexTextDef(field, field.getAnnotation(IndexText.class));
|
||||
indexTextDefMap.put(field.getName(), indexTextDef);
|
||||
}
|
||||
|
||||
return EntityDef.valueOf(idField, clazz, cacheSize, expireMillisecond, persisterStrategy, indexDefMap, indexTextDefMap);
|
||||
}
|
||||
|
||||
private void analyze(Class<?> clazz) {
|
||||
// 是否实现了IEntity接口
|
||||
AssertionUtils.isTrue(IEntity.class.isAssignableFrom(clazz), "被[{}]注解标注的实体类[{}]没有实现接口[{}]", EntityCache.class.getName(), clazz.getCanonicalName(), IEntity.class.getCanonicalName());
|
||||
// 实体类Entity必须被注解EntityCache标注
|
||||
AssertionUtils.notNull(clazz.getAnnotation(EntityCache.class), "实体类Entity[{}]必须被注解[{}]标注", clazz.getCanonicalName(), EntityCache.class.getName());
|
||||
|
||||
// 校验entity格式
|
||||
var entitySubClassMap = new HashMap<Class<?>, Set<Class<?>>>();
|
||||
checkEntity(clazz, entitySubClassMap);
|
||||
// 对象循环引用检测
|
||||
for (var entry : entitySubClassMap.entrySet()) {
|
||||
var subClass = entry.getKey();
|
||||
var subClassSet = entry.getValue();
|
||||
if (subClassSet.contains(subClass)) {
|
||||
throw new RunException("ORM[class:{}]在第一层包含循环引用对象[class:{}]", clazz.getSimpleName(), subClass.getSimpleName());
|
||||
}
|
||||
|
||||
var queue = new LinkedList<>(subClassSet);
|
||||
var allSubClassSet = new HashSet<>(queue);
|
||||
while (!queue.isEmpty()) {
|
||||
var firstSubClass = queue.poll();
|
||||
if (entitySubClassMap.containsKey(firstSubClass)) {
|
||||
for (var elementClass : entitySubClassMap.get(firstSubClass)) {
|
||||
if (subClass.equals(elementClass)) {
|
||||
throw new RunException("ORM[class:{}]在下层对象[class:{}]包含循环引用对象[class:{}]", clazz.getSimpleName(), elementClass.getSimpleName(), elementClass.getSimpleName());
|
||||
}
|
||||
|
||||
if (!allSubClassSet.contains(elementClass)) {
|
||||
allSubClassSet.add(elementClass);
|
||||
queue.offer(elementClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 校验id字段和id()方法的格式
|
||||
var idFields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class);
|
||||
AssertionUtils.isTrue(ArrayUtils.isNotEmpty(idFields) && idFields.length == 1, "实体类Entity[{}]必须只有且仅有一个Id注解", clazz.getSimpleName());
|
||||
var idField = idFields[0];
|
||||
// idField必须用private修饰
|
||||
AssertionUtils.isTrue(Modifier.isPrivate(idField.getModifiers()), "实体类Entity[{}]的id必须是private私有的", clazz.getSimpleName());
|
||||
if (clazz.isPrimitive() || Number.class.isAssignableFrom(clazz)) {
|
||||
var entityInstance = ReflectionUtils.newInstance(clazz);
|
||||
var idFieldType = idField.getType();
|
||||
if (idFieldType.equals(int.class) || idFieldType.equals(Integer.class)) {
|
||||
ReflectionUtils.setField(idField, entityInstance, RandomUtils.randomInt());
|
||||
} else if (idFieldType.equals(long.class) || idFieldType.equals(Long.class)) {
|
||||
ReflectionUtils.setField(idField, entityInstance, (long) RandomUtils.randomInt());
|
||||
} else if (idFieldType.equals(float.class) || idFieldType.equals(Float.class)) {
|
||||
ReflectionUtils.setField(idField, entityInstance, (float) RandomUtils.randomDouble());
|
||||
} else if (idFieldType.equals(double.class) || idFieldType.equals(Double.class)) {
|
||||
ReflectionUtils.setField(idField, entityInstance, (float) RandomUtils.randomDouble());
|
||||
} else if (idFieldType.equals(String.class)) {
|
||||
ReflectionUtils.setField(idField, entityInstance, RandomUtils.randomString(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEntity(Class<?> clazz, HashMap<Class<?>, Set<Class<?>>> entitySubClassMap) {
|
||||
// 不需要检查重复的协议
|
||||
if (entitySubClassMap.containsKey(clazz)) {
|
||||
return;
|
||||
}
|
||||
entitySubClassMap.put(clazz, new HashSet<>());
|
||||
|
||||
// 是否为一个简单的javabean
|
||||
ReflectionUtils.assertIsPojoClass(clazz);
|
||||
// 不能是泛型类
|
||||
AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}]不能是泛型类", clazz.getCanonicalName());
|
||||
// 必须要有一个空的构造器
|
||||
ReflectionUtils.publicEmptyConstructor(clazz);
|
||||
|
||||
|
||||
var filedList = Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(it -> !Modifier.isTransient(it.getModifiers()))
|
||||
.filter(it -> !Modifier.isStatic(it.getModifiers()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (var field : filedList) {
|
||||
// entity必须包含属性的get和set方法
|
||||
ReflectionUtils.fieldToGetMethod(clazz, field);
|
||||
ReflectionUtils.fieldToSetMethod(clazz, field);
|
||||
|
||||
// 是一个基本类型变量
|
||||
var fieldType = field.getType();
|
||||
if (isBaseType(fieldType)) {
|
||||
// do nothing
|
||||
} else if (fieldType.isArray()) {
|
||||
// 是一个数组
|
||||
Class<?> arrayClazz = fieldType.getComponentType();
|
||||
checkSubEntity(clazz, arrayClazz, entitySubClassMap);
|
||||
} else if (Set.class.isAssignableFrom(fieldType)) {
|
||||
AssertionUtils.isTrue(fieldType.equals(Set.class), "ORM[class:{}]类型声明不正确,必须是Set接口类型", clazz.getCanonicalName());
|
||||
|
||||
Type type = field.getGenericType();
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "ORM[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
AssertionUtils.isTrue(types.length == 1, "ORM[class:{}]中Set类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
checkSubEntity(clazz, types[0], entitySubClassMap);
|
||||
} else if (List.class.isAssignableFrom(fieldType)) {
|
||||
// 是一个List
|
||||
AssertionUtils.isTrue(fieldType.equals(List.class), "ORM[class:{}]类型声明不正确,必须是List接口类型", clazz.getCanonicalName());
|
||||
|
||||
Type type = field.getGenericType();
|
||||
AssertionUtils.isTrue(type instanceof ParameterizedType, "ORM[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
|
||||
AssertionUtils.isTrue(types.length == 1, "ORM[class:{}]中List类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
|
||||
|
||||
checkSubEntity(clazz, types[0], entitySubClassMap);
|
||||
} else if (Map.class.isAssignableFrom(fieldType)) {
|
||||
throw new RunException("ORM[class:{}]类型声明不正确,不支持Map类型", clazz.getCanonicalName());
|
||||
} else {
|
||||
entitySubClassMap.get(clazz).add(fieldType);
|
||||
checkEntity(fieldType, entitySubClassMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkSubEntity(Class<?> currentEntityClass, Type type, HashMap<Class<?>, Set<Class<?>>> entitySubClassMap) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
// 泛型类
|
||||
Class<?> clazz = (Class<?>) ((ParameterizedType) type).getRawType();
|
||||
if (Set.class.equals(clazz)) {
|
||||
// Set<Set<String>>
|
||||
checkSubEntity(currentEntityClass, ((ParameterizedType) type).getActualTypeArguments()[0], entitySubClassMap);
|
||||
return;
|
||||
} else if (List.class.equals(clazz)) {
|
||||
// List<List<String>>
|
||||
checkSubEntity(currentEntityClass, ((ParameterizedType) type).getActualTypeArguments()[0], entitySubClassMap);
|
||||
return;
|
||||
} else if (Map.class.equals(clazz)) {
|
||||
// Map<List<String>, List<String>>
|
||||
throw new RunException("ORM不支持Map类型");
|
||||
}
|
||||
} else if (type instanceof Class) {
|
||||
Class<?> clazz = ((Class<?>) type);
|
||||
if (isBaseType(clazz)) {
|
||||
// do nothing
|
||||
return;
|
||||
} else if (clazz.getComponentType() != null) {
|
||||
// 是一个二维以上数组
|
||||
throw new RunException("ORM不支持多维数组或集合嵌套数组[type:{}]类型,仅支持一维数组", type);
|
||||
} else if (clazz.equals(List.class) || clazz.equals(Set.class) || clazz.equals(Map.class)) {
|
||||
throw new RunException("ORM不支持数组和集合联合使用[type:{}]类型", type);
|
||||
} else {
|
||||
entitySubClassMap.get(currentEntityClass).add(clazz);
|
||||
checkEntity(clazz, entitySubClassMap);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RunException("[type:{}]类型不正确", type);
|
||||
}
|
||||
|
||||
private boolean isBaseType(Class<?> clazz) {
|
||||
return clazz.isPrimitive() || Number.class.isAssignableFrom(clazz) || String.class.isAssignableFrom(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,11 @@
|
||||
|
||||
package com.zfoo.orm.model.vo;
|
||||
|
||||
import com.zfoo.orm.model.anno.EntityCache;
|
||||
import com.zfoo.orm.model.anno.Id;
|
||||
import com.zfoo.orm.model.anno.Index;
|
||||
import com.zfoo.orm.model.anno.IndexText;
|
||||
import com.zfoo.orm.model.config.CacheStrategy;
|
||||
import com.zfoo.orm.model.config.PersisterStrategy;
|
||||
import com.zfoo.orm.model.entity.IEntity;
|
||||
import com.zfoo.protocol.collection.ArrayUtils;
|
||||
import com.zfoo.protocol.util.AssertionUtils;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -52,78 +40,19 @@ public class EntityDef {
|
||||
|
||||
private Map<String, IndexTextDef> indexTextDefMap;
|
||||
|
||||
private EntityDef() {
|
||||
}
|
||||
|
||||
public static EntityDef valueOf(Class<?> clazz, List<CacheStrategy> cacheStrategies, List<PersisterStrategy> persisterStrategies) {
|
||||
if (!IEntity.class.isAssignableFrom(clazz)) {
|
||||
throw new IllegalArgumentException(StringUtils.format("被[{}]注解标注的实体类[{}]必须继承[{}]", EntityCache.class.getName(), clazz.getName(), IEntity.class.getName()));
|
||||
}
|
||||
|
||||
public static EntityDef valueOf(Field idField, Class<? extends IEntity<?>> clazz, int cacheSize, long expireMillisecond
|
||||
, PersisterStrategy persisterStrategy, Map<String, IndexDef> indexDefMap, Map<String, IndexTextDef> indexTextDefMap) {
|
||||
var entityDef = new EntityDef();
|
||||
entityDef.clazz = (Class<? extends IEntity<?>>) clazz;
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(clazz.getDeclaredConstructor());
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(StringUtils.format("实体类Entity[{}]必须包含一个默认的构造器", clazz.getSimpleName()));
|
||||
}
|
||||
|
||||
var cache = clazz.getAnnotation(EntityCache.class);
|
||||
AssertionUtils.notNull(cache);
|
||||
var cacheStrategyOptional = cacheStrategies.stream().filter(it -> it.getStrategy().equals(cache.cacheStrategy())).findFirst();
|
||||
if (cacheStrategyOptional.isEmpty()) {
|
||||
throw new RuntimeException(StringUtils.format("实体类Entity[{}]没有找到缓存策略[{}]", clazz.getSimpleName(), cache.cacheStrategy()));
|
||||
}
|
||||
var cacheStrategy = cacheStrategyOptional.get();
|
||||
entityDef.cacheSize = cacheStrategy.getSize();
|
||||
entityDef.expireMillisecond = cacheStrategy.getExpireMillisecond();
|
||||
|
||||
var idFields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class);
|
||||
AssertionUtils.isTrue(ArrayUtils.isNotEmpty(idFields) && idFields.length == 1, "实体类Entity[{}]必须只有且仅有一个Id注解", clazz.getSimpleName());
|
||||
entityDef.idField = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class)[0];
|
||||
ReflectionUtils.makeAccessible(entityDef.idField);
|
||||
// idField必须用private修饰
|
||||
if (!Modifier.isPrivate(entityDef.idField.getModifiers())) {
|
||||
throw new RuntimeException(StringUtils.format("实体类Entity[{}]的id必须是private私有的", clazz.getSimpleName()));
|
||||
}
|
||||
|
||||
// entity必须包含属性的get和set方法
|
||||
Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(it -> !Modifier.isTransient(it.getModifiers()))
|
||||
.forEach(it -> {
|
||||
ReflectionUtils.fieldToGetMethod(clazz, it);
|
||||
ReflectionUtils.fieldToSetMethod(clazz, it);
|
||||
});
|
||||
var persister = cache.persister();
|
||||
AssertionUtils.notNull(persister);
|
||||
var persisterStrategyOptional = persisterStrategies.stream().filter(it -> it.getStrategy().equals(persister.value())).findFirst();
|
||||
if (persisterStrategyOptional.isEmpty()) {
|
||||
throw new RuntimeException(StringUtils.format("实体类Entity[{}]没有找到持久化策略[{}]", clazz.getSimpleName(), persister));
|
||||
}
|
||||
entityDef.persisterStrategy = persisterStrategyOptional.get();
|
||||
|
||||
var indexDefMap = new HashMap<String, IndexDef>();
|
||||
var fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Index.class);
|
||||
for (Field field : fields) {
|
||||
var indexAnnotation = field.getAnnotation(Index.class);
|
||||
IndexDef indexDef = new IndexDef(field, indexAnnotation.ascending(), indexAnnotation.unique());
|
||||
indexDefMap.put(field.getName(), indexDef);
|
||||
}
|
||||
|
||||
var indexTextDefMap = new HashMap<String, IndexTextDef>();
|
||||
fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, IndexText.class);
|
||||
for (Field field : fields) {
|
||||
IndexTextDef indexTextDef = new IndexTextDef(field, field.getAnnotation(IndexText.class));
|
||||
indexTextDefMap.put(field.getName(), indexTextDef);
|
||||
}
|
||||
|
||||
entityDef.idField = idField;
|
||||
entityDef.clazz = clazz;
|
||||
entityDef.cacheSize = cacheSize;
|
||||
entityDef.expireMillisecond = expireMillisecond;
|
||||
entityDef.persisterStrategy = persisterStrategy;
|
||||
entityDef.indexDefMap = indexDefMap;
|
||||
entityDef.indexTextDefMap = indexTextDefMap;
|
||||
|
||||
return entityDef;
|
||||
}
|
||||
|
||||
|
||||
public Class<? extends IEntity<?>> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.zfoo.orm.model.entity.IEntity;
|
||||
public class MailEnt implements IEntity<String> {
|
||||
|
||||
@Id
|
||||
private String mailId;
|
||||
private String id;
|
||||
|
||||
@Index(ascending = true, unique = false)
|
||||
private String userName;
|
||||
@@ -38,23 +38,23 @@ public class MailEnt implements IEntity<String> {
|
||||
public MailEnt() {
|
||||
}
|
||||
|
||||
public MailEnt(String mailId, String userName, String content) {
|
||||
this.mailId = mailId;
|
||||
public MailEnt(String id, String userName, String content) {
|
||||
this.id = id;
|
||||
this.userName = userName;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return mailId;
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMailId() {
|
||||
return mailId;
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setMailId(String mailId) {
|
||||
this.mailId = mailId;
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
@@ -73,12 +73,4 @@ public class MailEnt implements IEntity<String> {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MailEnt{" +
|
||||
"mailId='" + mailId + '\'' +
|
||||
", playName='" + userName + '\'' +
|
||||
", content='" + content + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ cpu: i9900k
|
||||
如果考虑支持修改协议类属性名称,必须让字段的读写顺序可控,这就需要注解来标识属性的顺序(protostuff就是这样做的),但是感觉这样不优雅。
|
||||
如果考虑支持字段增加和减少,需要消耗5%左右的性能(预估),并且增加一倍的包体积大小(写入字段的顺序),感觉不是非常划算。
|
||||
因为可以通过协议版本号来解决这个问题,所以去支持这样的增删操作动力并不是非常的大。
|
||||
|
||||
对于服务器来说协议一般有对内和对外的协议,对外protobuf用起来还行,但是服务器的内部调用protobuf用的就少了,用zfoo就可以统一对内和对外的协议。
|
||||
```
|
||||
|
||||
### Ⅵ. 协议规范
|
||||
|
||||
@@ -255,12 +255,8 @@ public class ProtocolAnalysis {
|
||||
AssertionUtils.isTrue(clazz.getSimpleName().matches("[a-zA-Z0-9_]*"), "[class:{}]的命名只能包含字母,数字,下划线", clazz.getCanonicalName(), PROTOCOL_ID);
|
||||
|
||||
// 必须要有一个空的构造器
|
||||
Constructor<?> constructor;
|
||||
try {
|
||||
constructor = clazz.getDeclaredConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new UnknownException(e, "[class:{}]协议序列号[{}]必须有一个空的构造器", clazz.getCanonicalName(), PROTOCOL_ID);
|
||||
}
|
||||
Constructor<?> constructor = ReflectionUtils.publicEmptyConstructor(clazz);
|
||||
|
||||
ReflectionUtils.makeAccessible(protocolIdField);
|
||||
IPacket packet = (IPacket) constructor.newInstance();
|
||||
|
||||
@@ -468,7 +464,7 @@ public class ProtocolAnalysis {
|
||||
} else if (List.class.equals(clazz)) {
|
||||
// List<List<String>>
|
||||
IFieldRegistration registration = typeToRegistration(currentProtocolClass, ((ParameterizedType) type).getActualTypeArguments()[0]);
|
||||
return ListField.valueOf(registration, (ParameterizedType) type);
|
||||
return ListField.valueOf(registration, type);
|
||||
} else if (Map.class.equals(clazz)) {
|
||||
// Map<List<String>, List<String>>
|
||||
IFieldRegistration keyRegistration = typeToRegistration(currentProtocolClass, ((ParameterizedType) type).getActualTypeArguments()[0]);
|
||||
|
||||
@@ -14,6 +14,7 @@ package com.zfoo.protocol.util;
|
||||
|
||||
import com.zfoo.protocol.collection.ArrayUtils;
|
||||
import com.zfoo.protocol.exception.RunException;
|
||||
import com.zfoo.protocol.exception.UnknownException;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
||||
/**
|
||||
* 反射工具类
|
||||
*
|
||||
@@ -81,6 +83,22 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static Constructor<?> publicEmptyConstructor(Class<?> clazz) {
|
||||
Constructor<?> constructor;
|
||||
|
||||
try {
|
||||
constructor = clazz.getDeclaredConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new UnknownException(e, "[class:{}] should have exactly one public zero-argument constructor", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
if (!Modifier.isPublic(constructor.getModifiers())) {
|
||||
throw new UnknownException("[class:{}] should have exactly one public zero-argument constructor", clazz.getCanonicalName());
|
||||
}
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准的属性名称更加通用,前缀不能是is,否则属性名称在不同语言很难去统一get和set方法
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user