diff --git a/storage/src/main/java/com/zfoo/storage/interpreter/JsonResource.java b/storage/src/main/java/com/zfoo/storage/interpreter/JsonResource.java new file mode 100644 index 00000000..7848c06e --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/interpreter/JsonResource.java @@ -0,0 +1,64 @@ +package com.zfoo.storage.interpreter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 配置文件资源 + * + */ +public class JsonResource { + // 文件名 + private String name; + //配置表字段名 + private List columns = new ArrayList<>(); + // 配置表数据 + private List> data = new ArrayList<>(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public List> getData() { + return data; + } + + public void setData(List> data) { + this.data = data; + } + + public static class ColumnMeta { + //字段名 + private String name; + //类型 + private String type; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } +} diff --git a/storage/src/main/java/com/zfoo/storage/interpreter/JsonResourceReader.java b/storage/src/main/java/com/zfoo/storage/interpreter/JsonResourceReader.java new file mode 100644 index 00000000..c442652b --- /dev/null +++ b/storage/src/main/java/com/zfoo/storage/interpreter/JsonResourceReader.java @@ -0,0 +1,156 @@ +package com.zfoo.storage.interpreter; + +import com.zfoo.protocol.exception.RunException; +import com.zfoo.protocol.util.JsonUtils; +import com.zfoo.protocol.util.ReflectionUtils; +import com.zfoo.protocol.util.StringUtils; +import com.zfoo.storage.model.anno.Id; +import com.zfoo.storage.strategy.*; + +import org.apache.commons.io.IOUtils; +import org.springframework.context.support.ConversionServiceFactoryBean; +import org.springframework.core.convert.TypeDescriptor; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +public class JsonResourceReader implements IResourceReader { + + private static final TypeDescriptor TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + + private static final ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean(); + + static { + var converters = new HashSet<>(); + converters.add(new JsonToArrayConverter()); + converters.add(new JsonToMapConverter()); + converters.add(new JsonToObjectConverter()); + converters.add(new StringToClassConverter()); + converters.add(new StringToDateConverter()); + converters.add(new StringToMapConverter()); + conversionServiceFactoryBean.setConverters(converters); + conversionServiceFactoryBean.afterPropertiesSet(); + } + + @Override + public List read(InputStream inputStream, Class clazz) { + String txt = readTxt(inputStream, clazz); + JsonResource JsonResource = JsonUtils.string2Object(txt, JsonResource.class); + + var result = new ArrayList(); + + // 默认取到第一个sheet页 + var fieldInfos = getFieldInfos(JsonResource, clazz); + + var iterator = JsonResource.getData().iterator(); + // 从ROW_SERVER这行开始读取数据 + while (iterator.hasNext()) { + var row = iterator.next(); + var instance = ReflectionUtils.newInstance(clazz); + + for (var fieldInfo : fieldInfos) { + var content = row.get(fieldInfo.index); + if (StringUtils.isNotEmpty(content) || fieldInfo.field.getType() == String.class) { + inject(instance, fieldInfo.field, content); + } + } + result.add(instance); + } + return result; + } + + private void inject(Object instance, Field field, String content) { + try { + var targetType = new TypeDescriptor(field); + var value = conversionServiceFactoryBean.getObject().convert(content, TYPE_DESCRIPTOR, targetType); + ReflectionUtils.makeAccessible(field); + ReflectionUtils.setField(field, instance, value); + } catch (Exception e) { + throw new RunException(e, "无法将Excel资源[class:{}]中的[content:{}]转换为属性[field:{}]", instance.getClass().getSimpleName(), content, field.getName()); + } + } + + + // 只读取代码里写的字段 + private Collection getFieldInfos(JsonResource resource, Class clazz) { + var fieldRow = resource.getColumns(); + if (fieldRow == null) { + throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", clazz.getSimpleName()); + } + + var cellFieldMap = new HashMap(); + for (var i = 0; i < fieldRow.size(); i++) { + var cell = fieldRow.get(i); + if (Objects.isNull(cell)) { + continue; + } + + var name = cell.getName(); + if (StringUtils.isEmpty(name)) { + continue; + } + var previousValue = cellFieldMap.put(name, i); + if (Objects.nonNull(previousValue)) { + throw new RunException("资源[class:{}]的Excel文件出现重复的属性控制列[field:{}]", clazz.getSimpleName(), name); + } + } + + var fieldList = Arrays.stream(clazz.getDeclaredFields()) + .filter(it -> !Modifier.isTransient(it.getModifiers())) + .filter(it -> !Modifier.isStatic(it.getModifiers())) + .collect(Collectors.toList()); + + for (var field : fieldList) { + if (!cellFieldMap.containsKey(field.getName())) { + throw new RunException("资源类[class:{}]的声明属性[filed:{}]无法获取,请检查配置表的格式", clazz, field.getName()); + } + + if (field.isAnnotationPresent(Id.class)) { + var cellIndex = cellFieldMap.get(field.getName()); + if (cellIndex != 0) { + throw new RunException("资源类[class:{}]的主键[Id:{}]必须放在Excel配置表的第一列,请检查配置表的格式", clazz, field.getName()); + } + } + + if (Modifier.isPublic(field.getModifiers())) { + throw new RunException("因为静态资源类是不能被修改的,所以资源类[class:{}]的属性[filed:{}]不能被public修饰,请改为private修饰", clazz, field.getName()); + } + + var setMethodName = StringUtils.EMPTY; + try { + setMethodName = ReflectionUtils.fieldToSetMethod(clazz, field); + } catch (Exception e) { + // 没有setMethod是正确的 + } + if (StringUtils.isNotBlank(setMethodName)) { + throw new RunException("因为静态资源类是不能被修改的,所以资源类[class:{}]的属性[filed:{}]不能含有set方法[{}]", clazz, field.getName(), setMethodName); + } + } + + return fieldList.stream().map(it -> new FieldInfo(cellFieldMap.get(it.getName()), it)).collect(Collectors.toList()); + + } + + private String readTxt(InputStream input, Class clazz) { + try { + return IOUtils.toString(input, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RunException("静态资源[{}]异常,无法读取文件", clazz.getSimpleName()); + } + } + + private static class FieldInfo { + public final int index; + public final Field field; + + public FieldInfo(int index, Field field) { + this.index = index; + this.field = field; + } + } +} diff --git a/storage/src/main/java/com/zfoo/storage/schema/StorageDefinitionParser.java b/storage/src/main/java/com/zfoo/storage/schema/StorageDefinitionParser.java index ca632488..db59383d 100644 --- a/storage/src/main/java/com/zfoo/storage/schema/StorageDefinitionParser.java +++ b/storage/src/main/java/com/zfoo/storage/schema/StorageDefinitionParser.java @@ -17,6 +17,7 @@ import com.zfoo.protocol.util.DomUtils; import com.zfoo.protocol.util.StringUtils; import com.zfoo.storage.StorageContext; import com.zfoo.storage.interpreter.ExcelResourceReader; +import com.zfoo.storage.interpreter.JsonResourceReader; import com.zfoo.storage.manager.StorageManager; import com.zfoo.storage.model.config.StorageConfig; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -72,6 +73,17 @@ public class StorageDefinitionParser implements BeanDefinitionParser { resolvePlaceholder("suffix", "resourceSuffix", builder, resourceElement, parserContext); parserContext.getRegistry().registerBeanDefinition(clazz.getCanonicalName(), builder.getBeanDefinition()); + + // 注册ExcelResourceReader + Class readerClazz = ExcelResourceReader.class; + var resourceSuffix = resourceElement.getAttribute("suffix"); + if (resourceSuffix.equals("txt")) { + readerClazz = JsonResourceReader.class; + } + + var readerName = StringUtils.uncapitalize(readerClazz.getName()); + builder = BeanDefinitionBuilder.rootBeanDefinition(readerClazz); + parserContext.getRegistry().registerBeanDefinition(readerName, builder.getBeanDefinition()); } private void registerBeanDefinition(ParserContext parserContext) { @@ -88,10 +100,10 @@ public class StorageDefinitionParser implements BeanDefinitionParser { registry.registerBeanDefinition(name, builder.getBeanDefinition()); // 注册ExcelResourceReader - clazz = ExcelResourceReader.class; - name = StringUtils.uncapitalize(clazz.getName()); - builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); - registry.registerBeanDefinition(name, builder.getBeanDefinition()); +// clazz = ExcelResourceReader.class; +// name = StringUtils.uncapitalize(clazz.getName()); +// builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); +// registry.registerBeanDefinition(name, builder.getBeanDefinition()); } private void resolvePlaceholder(String attributeName, String fieldName, BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { diff --git a/storage/src/main/java/com/zfoo/storage/strategy/JsonToMapConverter.java b/storage/src/main/java/com/zfoo/storage/strategy/JsonToMapConverter.java index 28ef7f50..a65c035b 100644 --- a/storage/src/main/java/com/zfoo/storage/strategy/JsonToMapConverter.java +++ b/storage/src/main/java/com/zfoo/storage/strategy/JsonToMapConverter.java @@ -39,7 +39,8 @@ public class JsonToMapConverter implements ConditionalGenericConverter { @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { String content = (String) source; - return JsonUtils.string2Object(content, targetType.getType()); + return JsonUtils.string2Map(content, targetType.getMapKeyTypeDescriptor().getType(), targetType.getMapValueTypeDescriptor().getType()); +// return JsonUtils.string2Object(content, targetType.getType()); // return JsonUtil.string2Map(content, targetType.getMapKeyTypeDescriptor().getType() // , targetType.getMapValueTypeDescriptor().getType()); } diff --git a/storage/src/test/java/com/zfoo/storage/ApplicationTest.java b/storage/src/test/java/com/zfoo/storage/ApplicationTest.java index 4935b898..7bf9dc3f 100644 --- a/storage/src/test/java/com/zfoo/storage/ApplicationTest.java +++ b/storage/src/test/java/com/zfoo/storage/ApplicationTest.java @@ -22,6 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; +import java.util.Map; + /** * @author jaysunxiao * @version 3.0 @@ -61,4 +63,26 @@ public class ApplicationTest { var valuesByIndex = studentResources.getIndex("name", "james0"); logger.info(JsonUtils.object2String(valuesByIndex)); } + + @Test + public void startStorageTestTxt() { + // 加载配置文件,配置文件中必须引入storage + // 配置文件中scan,需要映射Excel的类所在位置,会自动搜索文件夹下的Excel文件,Excel文件可以放在指定文件夹的任意目录 + // 配置文件中resource,需要映射Excel的文件所在位置 + var context = new ClassPathXmlApplicationContext("application.xml"); + + // Excel的映射内容需要在被Spring管理的bean的方法上加上@ResInjection注解,即可自动注入Excel对应的对象 + var testManager = context.getBean(TestManager.class); + var testResources = testManager.testResources; + for (com.zfoo.storage.resource.TestResource resource : testResources.getAll()) { + Map map = resource.getType9(); + logger.info(map.get(1)); +// logger.info(JsonUtils.object2String(resource)); + } + // 通过id找到对应的行 + var id = 2; + var valueById = testResources.get(id); + logger.info(JsonUtils.object2String(valueById)); + logger.info(StringUtils.MULTIPLE_HYPHENS); + } } diff --git a/storage/src/test/java/com/zfoo/storage/StudentManager.java b/storage/src/test/java/com/zfoo/storage/StudentManager.java index a3f27229..d0801284 100644 --- a/storage/src/test/java/com/zfoo/storage/StudentManager.java +++ b/storage/src/test/java/com/zfoo/storage/StudentManager.java @@ -22,7 +22,7 @@ import org.springframework.stereotype.Component; * @author jaysunxiao * @version 3.0 */ -@Component +//@Component public class StudentManager { @ResInjection diff --git a/storage/src/test/java/com/zfoo/storage/TestManager.java b/storage/src/test/java/com/zfoo/storage/TestManager.java new file mode 100644 index 00000000..fe3c9a29 --- /dev/null +++ b/storage/src/test/java/com/zfoo/storage/TestManager.java @@ -0,0 +1,33 @@ +/* + * 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; + +import com.zfoo.storage.model.anno.ResInjection; +import com.zfoo.storage.model.vo.Storage; +import com.zfoo.storage.resource.StudentResource; +import com.zfoo.storage.resource.TestResource; + +import org.springframework.stereotype.Component; + +/** + * @author jaysunxiao + * @version 3.0 + */ +@Component +public class TestManager { + + @ResInjection + public Storage testResources; + +} diff --git a/storage/src/test/java/com/zfoo/storage/resource/Item.java b/storage/src/test/java/com/zfoo/storage/resource/Item.java new file mode 100644 index 00000000..9ef9c45c --- /dev/null +++ b/storage/src/test/java/com/zfoo/storage/resource/Item.java @@ -0,0 +1,37 @@ +/* + * 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.resource; + +/** + * @author jaysunxiao + * @version 3.0 + */ +public class Item { + + private int id; + private int attr; + private String name; + + public int getId() { + return id; + } + + public int getAttr() { + return attr; + } + + public String getName() { + return name; + } +} \ No newline at end of file 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 172c26e3..88039ac7 100644 --- a/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java +++ b/storage/src/test/java/com/zfoo/storage/resource/StudentResource.java @@ -21,7 +21,7 @@ import com.zfoo.storage.model.anno.Resource; * @author jaysunxiao * @version 3.0 */ -@Resource +//@Resource public class StudentResource { @Id diff --git a/storage/src/test/java/com/zfoo/storage/resource/TestResource.java b/storage/src/test/java/com/zfoo/storage/resource/TestResource.java new file mode 100644 index 00000000..beb01ee0 --- /dev/null +++ b/storage/src/test/java/com/zfoo/storage/resource/TestResource.java @@ -0,0 +1,72 @@ +/* + * 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.resource; + +import java.util.List; +import java.util.Map; + +import com.zfoo.storage.model.anno.Id; +import com.zfoo.storage.model.anno.Index; +import com.zfoo.storage.model.anno.Resource; + +/** + * @author jaysunxiao + * @version 3.0 + */ +@Resource +public class TestResource { + + @Id + private int Id; + private long Type0; + private String Type1; + private String[] Type2; + private Integer[] Type3; + private Long[] Type4; + private Item[] Type5; + private Map Type8; + private Map Type9; + private Map Type10; + + public int getId() { + return Id; + } + public long getType0() { + return Type0; + } + public String getType1() { + return Type1; + } + public String[] getType2() { + return Type2; + } + public Integer[] getType3() { + return Type3; + } + public Long[] getType4() { + return Type4; + } + public Item[] getType5() { + return Type5; + } + public Map getType8() { + return Type8; + } + public Map getType9() { + return Type9; + } + public Map getType10() { + return Type10; + } +} diff --git a/storage/src/test/resources/application.xml b/storage/src/test/resources/application.xml index b939ea6b..819e37fd 100644 --- a/storage/src/test/resources/application.xml +++ b/storage/src/test/resources/application.xml @@ -49,7 +49,8 @@ - + + diff --git a/storage/src/test/resources/excel/TestResource.txt b/storage/src/test/resources/excel/TestResource.txt new file mode 100644 index 00000000..de49bb5d --- /dev/null +++ b/storage/src/test/resources/excel/TestResource.txt @@ -0,0 +1,83 @@ +{ + "name": "Test", + "columns": [ + { + "name": "Id", + "type": "int" + }, + { + "name": "Type0", + "type": "long" + }, + { + "name": "Type1", + "type": "string" + }, + { + "name": "Type2", + "type": "list\u003cstring\u003e" + }, + { + "name": "Type3", + "type": "list\u003cint\u003e" + }, + { + "name": "Type4", + "type": "list\u003clong\u003e" + }, + { + "name": "Type5", + "type": "array\u003cattr,id,name\u003e" + }, + { + "name": "Type8", + "type": "map\u003cint,int\u003e" + }, + { + "name": "Type9", + "type": "map\u003cint,string\u003e" + }, + { + "name": "Type10", + "type": "map\u003cstring,string\u003e" + } + ], + "data": [ + [ + "1", + "0", + "1", + "[\"\\\"sada\\\"\"]", + "[21321]", + "[100000000000000,30000000000000]", + "[{\"name\":\"num\",\"id\":5,\"attr\":10},{\"name\":\"num\",\"id\":5,\"attr\":10}]", + "{\"1\":10}", + "{\"1\":\"\\\"cn\\\"\"}", + "{\"\\\"chinese\\\"\":\"\\\"cn\\\"\"}" + ], + [ + "2", + "0", + "3", + "[\"\\\"sada\\\"\",\"\\\"sader\\\"\"]", + "[21,321]", + "[100000000000000,30000000000000]", + "[{\"name\":\"num\",\"id\":5,\"attr\":10},{\"name\":\"num\",\"id\":5,\"attr\":10}]", + "{\"1\":10,\"2\":50}", + "{\"1\":\"\\\"cn\\\"\",\"2\":\"\\\"en\\\"\"}", + "{\"\\\"english\\\"\":\"\\\"en\\\"\",\"\\\"chinese\\\"\":\"\\\"cn\\\"\"}" + ], + [ + "3", + "0", + "2", + "[\"\\\"sada\\\"\",\"\\\"s\\\"\",\"\\\"ader\\\"\"]", + "[21,3,21]", + "[100000000000000,30000000000000]", + "[{\"name\":\"num\",\"id\":5,\"attr\":10},{\"name\":\"num\",\"id\":5,\"attr\":10}]", + "{\"1\":10,\"2\":50,\"3\":100}", + "{\"1\":\"\\\"cn\\\"\",\"2\":\"\\\"en\\\"\",\"3\":\"\\\"ru\\\"\"}", + "{\"\\\"Ruisn\\\"\":\"\\\"ru\\\"\",\"\\\"english\\\"\":\"\\\"en\\\"\",\"\\\"chinese\\\"\":\"\\\"cn\\\"\"}" + ] + ] +} \ No newline at end of file