From 1647b99ccf6ab566af7c193f71ba5f29c68b80bd Mon Sep 17 00:00:00 2001 From: jaysunxiao Date: Fri, 30 Jul 2021 22:39:08 +0800 Subject: [PATCH] =?UTF-8?q?perf[orm]:=20=E5=BE=AA=E7=8E=AF=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E6=A0=A1=E9=AA=8C=E5=92=8C=E6=A0=BC=E5=BC=8F=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E5=88=86=E6=9E=90=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orm/README.md | 3 +- .../java/com/zfoo/orm/manager/OrmManager.java | 233 ++++++++++++++++-- .../java/com/zfoo/orm/model/vo/EntityDef.java | 85 +------ .../java/com/zfoo/orm/entity/MailEnt.java | 24 +- protocol/README.md | 2 + .../registration/ProtocolAnalysis.java | 10 +- .../zfoo/protocol/util/ReflectionUtils.java | 18 ++ 7 files changed, 259 insertions(+), 116 deletions(-) diff --git a/orm/README.md b/orm/README.md index 52a7b8cd..ef0642a4 100644 --- a/orm/README.md +++ b/orm/README.md @@ -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对象定义将无法启动程序并给出错误警告, ### Ⅲ. 使用方法 diff --git a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java index 270d974e..da5e8eb2 100644 --- a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java +++ b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java @@ -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>, EntityDef> scanEntity() { + private Map>, EntityDef> scanEntityClass() { var cacheDefMap = new HashMap>, 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> entityClazz; try { - entityClazz = Class.forName(location); + entityClazz = (Class>) 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>) entityClazz, cacheDef); + var cacheDef = parserEntityDef(entityClazz); + var previousCacheDef = cacheDefMap.putIfAbsent(entityClazz, cacheDef); AssertionUtils.isNull(previousCacheDef, "缓存实体不能包含重复的[class:{}]", entityClazz.getSimpleName()); } return cacheDefMap; } - private Set getEntityLocation(String scanLocation) { + private Set 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> 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(); + 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(); + 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, Set>>(); + 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, Set>> 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, Set>> entitySubClassMap) { + if (type instanceof ParameterizedType) { + // 泛型类 + Class clazz = (Class) ((ParameterizedType) type).getRawType(); + if (Set.class.equals(clazz)) { + // Set> + checkSubEntity(currentEntityClass, ((ParameterizedType) type).getActualTypeArguments()[0], entitySubClassMap); + return; + } else if (List.class.equals(clazz)) { + // List> + checkSubEntity(currentEntityClass, ((ParameterizedType) type).getActualTypeArguments()[0], entitySubClassMap); + return; + } else if (Map.class.equals(clazz)) { + // Map, List> + 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); + } } diff --git a/orm/src/main/java/com/zfoo/orm/model/vo/EntityDef.java b/orm/src/main/java/com/zfoo/orm/model/vo/EntityDef.java index 57979ee4..2652b9fc 100644 --- a/orm/src/main/java/com/zfoo/orm/model/vo/EntityDef.java +++ b/orm/src/main/java/com/zfoo/orm/model/vo/EntityDef.java @@ -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 indexTextDefMap; - private EntityDef() { - } - - public static EntityDef valueOf(Class clazz, List cacheStrategies, List 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> clazz, int cacheSize, long expireMillisecond + , PersisterStrategy persisterStrategy, Map indexDefMap, Map indexTextDefMap) { var entityDef = new EntityDef(); - entityDef.clazz = (Class>) 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(); - 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(); - 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> getClazz() { return clazz; } diff --git a/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java b/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java index b6e0427e..2d91b451 100644 --- a/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java +++ b/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java @@ -28,7 +28,7 @@ import com.zfoo.orm.model.entity.IEntity; public class MailEnt implements IEntity { @Id - private String mailId; + private String id; @Index(ascending = true, unique = false) private String userName; @@ -38,23 +38,23 @@ public class MailEnt implements IEntity { 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 { this.content = content; } - @Override - public String toString() { - return "MailEnt{" + - "mailId='" + mailId + '\'' + - ", playName='" + userName + '\'' + - ", content='" + content + '\'' + - '}'; - } } diff --git a/protocol/README.md b/protocol/README.md index 3b068fe8..c083ca11 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -73,6 +73,8 @@ cpu: i9900k 如果考虑支持修改协议类属性名称,必须让字段的读写顺序可控,这就需要注解来标识属性的顺序(protostuff就是这样做的),但是感觉这样不优雅。 如果考虑支持字段增加和减少,需要消耗5%左右的性能(预估),并且增加一倍的包体积大小(写入字段的顺序),感觉不是非常划算。 因为可以通过协议版本号来解决这个问题,所以去支持这样的增删操作动力并不是非常的大。 + +对于服务器来说协议一般有对内和对外的协议,对外protobuf用起来还行,但是服务器的内部调用protobuf用的就少了,用zfoo就可以统一对内和对外的协议。 ``` ### Ⅵ. 协议规范 diff --git a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java index 1ad95cdd..2f7232fc 100644 --- a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java +++ b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java @@ -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> 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> IFieldRegistration keyRegistration = typeToRegistration(currentProtocolClass, ((ParameterizedType) type).getActualTypeArguments()[0]); 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 7c1fe588..75759ccf 100644 --- a/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java @@ -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方法 */