Merge branch 'main' of github.com:zfoo-project/zfoo

This commit is contained in:
jianan committed 2022-07-08 12:10:48 +08:00
commit 2f16356414
26 files changed
+443 -323

No files matched your search

@@ -13,7 +13,7 @@
package com.zfoo.boot;
import com.zfoo.storage.StorageContext;
import com.zfoo.storage.interpreter.ExcelResourceReader;
import com.zfoo.storage.interpreter.ResourceReader;
import com.zfoo.storage.manager.StorageManager;
import com.zfoo.storage.model.config.StorageConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -40,8 +40,8 @@ public class StorageAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ExcelResourceReader excelResourceReader() {
return new ExcelResourceReader();
public ResourceReader excelResourceReader() {
return new ResourceReader();
}
@Bean
+2
View File
@@ -15,6 +15,8 @@
安装好以后,在git bash中输入:git --version,有结果返回则表示安装成功
```
- 参看所有的git配置,git config --list
- git下载代码慢的解决方法|无法下载代码的解决方法,取消git配置使用git config --global --unset http.proxy
```
@@ -55,8 +55,8 @@ public abstract class GenerateProtocolPath {
public static String getRelativePath(short protocolId, short relativeProtocolId) {
// 不是折叠协议的话,protocolPathMap一定是空,这里返回“”,上层会解析为同一个文件下
if(protocolPathMap.size() == 0){
return "";
if(CollectionUtils.isEmpty(protocolPathMap)){
return StringUtils.EMPTY;
}
var protocolPath = protocolPathMap.get(protocolId);
var relativePath = protocolPathMap.get(relativeProtocolId);
@@ -14,9 +14,13 @@ package com.zfoo.protocol.util;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
import com.zfoo.protocol.exception.RunException;
@@ -53,6 +57,13 @@ public abstract class JsonUtils {
//反序列化
//当反序列化有未知属性则抛异常,true打开这个设置
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
//美化输出
SerializationConfig config = MAPPER.getSerializationConfig();
PrettyPrinter prettyPrinter = config.getDefaultPrettyPrinter();
DefaultPrettyPrinter defpp = (DefaultPrettyPrinter) prettyPrinter;
DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter(" ", DefaultIndenter.SYS_LF);
defpp.indentArraysWith(indenter);
defpp.indentObjectsWith(indenter);
MAPPER_TURBO.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MAPPER_TURBO.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
@@ -68,6 +79,7 @@ public abstract class JsonUtils {
}
}
//普通输出
public static String object2String(Object object) {
try {
return MAPPER.writeValueAsString(object);
@@ -76,6 +88,15 @@ public abstract class JsonUtils {
}
}
//格式化/美化/优雅的输出
public static String object2StringPrettyPrinter(Object object) {
try {
return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
} catch (Exception e) {
throw new RunException(e, "将对象[object:{}]转换为json字符串时异常", object);
}
}
/**
* 涡轮增压极大提升json转换性能,字节码增强
*/
@@ -193,9 +214,9 @@ public abstract class JsonUtils {
// 循环遍历子节点下的信息
while (iterator.hasNext()) {
var node = iterator.next();
var field = node.getKey();
var filed = node.getKey();
var value = node.getValue().asText();
jsonMap.put(field, value);
jsonMap.put(filed, value);
}
}
return jsonMap;
@@ -20,13 +20,13 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public class XmlProtocolDefinition {
@JacksonXmlProperty(isAttribute = true, localName = "id")
private final short id = -1;
private short id = -1;
@JacksonXmlProperty(isAttribute = true, localName = "location")
private String location;
@JacksonXmlProperty(isAttribute = true, localName = "enhance")
private final boolean enhance = true;
private boolean enhance = true;
public short getId() {
@@ -22,23 +22,23 @@ public class DomUtilsTest {
private static final String XML_WITH_HEAD = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" +
"\n" +
"<protocols author=\"jaysunxiao\">\n" +
" <module id=\"1\" name=\"common\" minId=\"1000\" maxId=\"2000\" version=\"1.0.0\">\n" +
" <module id=\"1\" name=\"common\" minId=\"1000\" maxId=\"2000\">\n" +
" <protocol id=\"1000\" location=\"com.zfoo.test.CM_Int\"/>\n" +
" <protocol id=\"2000\" location=\"com.zfoo.test.SM_Int\"/>\n" +
" </module>\n" +
"\n" +
" <module id=\"2\" name=\"common\" minId=\"2000\" maxId=\"3000\" version=\"1.0.0\">\n" +
" <module id=\"2\" name=\"common\" minId=\"2000\" maxId=\"3000\">\n" +
" <protocol id=\"3000\" location=\"com.zfoo.test.CM_Float\"/>\n" +
" </module>\n" +
"</protocols>";
private static final String XML_OF_STANDARD_TEXT = "<protocols author=\"jaysunxiao\">\n" +
" <module id=\"1\" name=\"common\" minId=\"1000\" maxId=\"2000\" version=\"1.0.0\">\n" +
" <module id=\"1\" name=\"common\" minId=\"1000\" maxId=\"2000\">\n" +
" <protocol id=\"1000\" location=\"com.zfoo.test.CM_Int\"/>\n" +
" <protocol id=\"2000\" location=\"com.zfoo.test.SM_Int\"/>\n" +
" </module>\n" +
"\n" +
" <module id=\"2\" name=\"common\" minId=\"2000\" maxId=\"3000\" version=\"1.0.0\">\n" +
" <module id=\"2\" name=\"common\" minId=\"2000\" maxId=\"3000\">\n" +
" <protocol id=\"3000\" location=\"com.zfoo.test.CM_Float\"/>\n" +
" </module>\n" +
"</protocols>";
@@ -27,6 +27,6 @@ import java.util.List;
*/
public interface IResourceReader {
<T> List<T> read(InputStream inputStream, Class<T> clazz);
<T> List<T> read(InputStream inputStream, Class<T> clazz, String suffix);
}
@@ -1,156 +0,0 @@
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 <T> List<T> read(InputStream inputStream, Class<T> clazz) {
String txt = readTxt(inputStream, clazz);
JsonResource JsonResource = JsonUtils.string2Object(txt, JsonResource.class);
var result = new ArrayList<T>();
// 默认取到第一个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<FieldInfo> getFieldInfos(JsonResource resource, Class<?> clazz) {
var fieldRow = resource.getColumns();
if (fieldRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", clazz.getSimpleName());
}
var cellFieldMap = new HashMap<String, Integer>();
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;
}
}
}
@@ -7,11 +7,11 @@ import java.util.List;
* 配置文件资源
*
*/
public class JsonResource {
public class ResourceConfig {
// 文件名
private String name;
//配置表字段名
private List<ColumnMeta> columns = new ArrayList<>();
private List<Header> header = new ArrayList<>();
// 配置表数据
private List<List<String>> data = new ArrayList<>();
@@ -23,12 +23,12 @@ public class JsonResource {
this.name = name;
}
public List<ColumnMeta> getColumns() {
return columns;
public List<Header> getHeader() {
return header;
}
public void setColumns(List<ColumnMeta> columns) {
this.columns = columns;
public void setHeader(List<Header> header) {
this.header = header;
}
public List<List<String>> getData() {
@@ -39,12 +39,23 @@ public class JsonResource {
this.data = data;
}
public static class ColumnMeta {
public static class Header {
//字段名
private String name;
//类型
private String type;
//列
private int index;
public Header() {
}
public Header(String name, String type, int index) {
this.name = name;
this.type = type;
this.index = index;
}
public String getName() {
return name;
}
@@ -60,5 +71,13 @@ public class JsonResource {
public void setType(String type) {
this.type = type;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
}
@@ -14,12 +14,15 @@
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.interpreter.ResourceConfig.Header;
import com.zfoo.storage.model.anno.Id;
import com.zfoo.storage.strategy.*;
import com.zfoo.storage.util.CellUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
@@ -30,6 +33,7 @@ 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;
@@ -37,7 +41,7 @@ import java.util.stream.Collectors;
* @author jaysunxiao
* @version 3.0
*/
public class ExcelResourceReader implements IResourceReader {
public class ResourceReader implements IResourceReader {
private static final TypeDescriptor TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
@@ -56,33 +60,27 @@ public class ExcelResourceReader implements IResourceReader {
}
@Override
public <T> List<T> read(InputStream inputStream, Class<T> clazz) {
var wb = createWorkbook(inputStream, clazz);
public <T> List<T> read(InputStream inputStream, Class<T> clazz, String suffix) {
ResourceConfig resource = null;
if (suffix.equals("txt")) {
resource = readJson(inputStream, clazz.getSimpleName());
} else {
resource = readExcel(inputStream, clazz.getSimpleName());
}
var result = new ArrayList<T>();
//获取所有字段
var cellFieldMap = getFieldMap(resource, clazz);
var fieldInfos = getFieldInfos(cellFieldMap, clazz);
// 默认取到第一个sheet页
var sheet = wb.getSheetAt(0);
var fieldInfos = getFieldInfos(sheet, clazz);
var iterator = sheet.iterator();
// 行数定位到有效数据行,默认是第四行为有效数据行
iterator.next();
iterator.next();
iterator.next();
var iterator = resource.getData().iterator();
// 从ROW_SERVER这行开始读取数据
while (iterator.hasNext()) {
var row = iterator.next();
var instance = ReflectionUtils.newInstance(clazz);
var idCell = row.getCell(0);
if (StringUtils.isBlank(CellUtils.getCellStringValue(idCell))) {
continue;
}
for (var fieldInfo : fieldInfos) {
var cell = row.getCell(fieldInfo.index);
var content = CellUtils.getCellStringValue(cell);
var content = row.get(fieldInfo.index);
if (StringUtils.isNotEmpty(content) || fieldInfo.field.getType() == String.class) {
inject(instance, fieldInfo.field, content);
}
@@ -91,7 +89,81 @@ public class ExcelResourceReader implements IResourceReader {
}
return result;
}
public ResourceConfig readExcel(InputStream inputStream, String name) {
var wb = createWorkbook(inputStream, name);
var resource = new ResourceConfig();
resource.setName(name);
// 默认取到第一个sheet页
var sheet = wb.getSheetAt(0);
//设置所有列
var headers = getHeaders(sheet, name);
resource.setHeader(headers);
// 行数定位到有效数据行,默认是第四行为有效数据行
var iterator = sheet.iterator();
iterator.next();
iterator.next();
iterator.next();
// 从ROW_SERVER这行开始读取数据
List<List<String>> data = new ArrayList<>();
while (iterator.hasNext()) {
var row = iterator.next();
List<String> rowData = new ArrayList<>();
for (var header : headers) {
var cell = row.getCell(header.getIndex());
var content = CellUtils.getCellStringValue(cell);
rowData.add(content);
}
data.add(rowData);
}
resource.setData(data);
return resource;
}
// 只读取代码里写的字段
private List<Header> getHeaders(Sheet sheet, String fileName) {
var iterator = sheet.iterator();
// 获取配置表的有效列名称,默认第一行就是字段名称
var fieldRow = iterator.next();
if (fieldRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", fileName);
}
//默认第二行字段类型
var typeRow = iterator.next();
if (typeRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的类型控制列", fileName);
}
var headerList = new ArrayList<Header>();
var cellFieldMap = new HashMap<String, Integer>();
for (var i = 0; i < fieldRow.getLastCellNum(); i++) {
var fieldCell = fieldRow.getCell(i);
if (Objects.isNull(fieldCell)) {
continue;
}
var typeCell = typeRow.getCell(i);
if (Objects.isNull(typeCell)) {
continue;
}
var fieldName = CellUtils.getCellStringValue(fieldCell);
if (StringUtils.isEmpty(fieldName)) {
continue;
}
var typeName = CellUtils.getCellStringValue(typeCell);
if (StringUtils.isEmpty(typeName)) {
continue;
}
var previousValue = cellFieldMap.put(fieldName, i);
if (Objects.nonNull(previousValue)) {
throw new RunException("资源[class:{}]的Excel文件出现重复的属性控制列[field:{}]", fileName, fieldName);
}
headerList.add(new Header(fieldName, typeName, i));
}
return headerList;
}
private void inject(Object instance, Field field, String content) {
try {
var targetType = new TypeDescriptor(field);
@@ -103,43 +175,20 @@ public class ExcelResourceReader implements IResourceReader {
}
}
// 只读取代码里写的字段
private Collection<FieldInfo> getFieldInfos(Sheet sheet, Class<?> clazz) {
var fieldRow = getFieldRow(sheet);
if (fieldRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", clazz.getSimpleName());
}
var cellFieldMap = new HashMap<String, Integer>();
for (var i = 0; i < fieldRow.getLastCellNum(); i++) {
var cell = fieldRow.getCell(i);
if (Objects.isNull(cell)) {
continue;
}
var name = CellUtils.getCellStringValue(cell);
if (StringUtils.isEmpty(name)) {
continue;
}
var previousValue = cellFieldMap.put(name, i);
if (Objects.nonNull(previousValue)) {
throw new RunException("资源[class:{}]的Excel文件出现重复的属性控制列[field:{}]", clazz.getSimpleName(), name);
}
}
private Collection<FieldInfo> getFieldInfos(Map<String, Integer> fieldMap, Class<?> clazz) {
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())) {
if (!fieldMap.containsKey(field.getName())) {
throw new RunException("资源类[class:{}]的声明属性[filed:{}]无法获取,请检查配置表的格式", clazz, field.getName());
}
if (field.isAnnotationPresent(Id.class)) {
var cellIndex = cellFieldMap.get(field.getName());
var cellIndex = fieldMap.get(field.getName());
if (cellIndex != 0) {
throw new RunException("资源类[class:{}]的主键[Id:{}]必须放在Excel配置表的第一列,请检查配置表的格式", clazz, field.getName());
}
@@ -160,27 +209,17 @@ public class ExcelResourceReader implements IResourceReader {
}
}
return fieldList.stream().map(it -> new FieldInfo(cellFieldMap.get(it.getName()), it)).collect(Collectors.toList());
return fieldList.stream().map(it -> new FieldInfo(fieldMap.get(it.getName()), it)).collect(Collectors.toList());
}
// 获取配置表的有效列名称,默认第一行就是字段名称
private Row getFieldRow(Sheet sheet) {
var iterator = sheet.iterator();
var row = iterator.next();
return row;
}
private Workbook createWorkbook(InputStream input, Class<?> clazz) {
private Workbook createWorkbook(InputStream input, String name) {
try {
return WorkbookFactory.create(input);
} catch (IOException e) {
throw new RunException("静态资源[{}]异常,无法读取文件", clazz.getSimpleName());
throw new RunException("静态资源[{}]异常,无法读取文件", name);
}
}
private static class FieldInfo {
public final int index;
public final Field field;
@@ -190,4 +229,39 @@ public class ExcelResourceReader implements IResourceReader {
this.field = field;
}
}
public Map<String, Integer> getFieldMap(ResourceConfig resource, Class<?> clazz) {
var header = resource.getHeader();
if (header == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", clazz.getSimpleName());
}
var cellFieldMap = new HashMap<String, Integer>();
for (var i = 0; i < header.size(); i++) {
var cell = header.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);
}
}
return cellFieldMap;
}
private ResourceConfig readJson(InputStream input, String name) {
try {
var jsonStr = IOUtils.toString(input, StandardCharsets.UTF_8);
//将json字符转换成对象
return JsonUtils.string2Object(jsonStr, ResourceConfig.class);
} catch (IOException e) {
throw new RunException("静态资源[{}]异常,无法读取文件", name);
}
}
}
@@ -16,6 +16,7 @@ package com.zfoo.storage.manager;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.exception.ExceptionUtils;
import com.zfoo.protocol.exception.RunException;
import com.zfoo.protocol.util.FileUtils;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
@@ -98,8 +99,10 @@ public class StorageManager implements IStorageManager {
try {
for (var definition : resourceDefinitionMap.values()) {
var clazz = definition.getClazz();
var resource = definition.getResource();
var fileExtName = FileUtils.fileExtName(resource.getFilename());
Storage<?, ?> storage = new Storage<>();
storage.init(definition.getResource().getInputStream(), definition.getClazz());
storage.init(resource.getInputStream(), definition.getClazz(), fileExtName);
storageMap.putIfAbsent(clazz, storage);
allStorageUsableMap.put(clazz, false);
}
@@ -224,7 +227,7 @@ public class StorageManager implements IStorageManager {
try {
var resourceList = new ArrayList<Resource>();
var packageSearchPath = StringUtils.format("{}/**/{}.{}", storageConfig.getResourceLocation(), clazz.getSimpleName(), storageConfig.getResourceSuffix());
var packageSearchPath = StringUtils.format("{}/**/{}.*", storageConfig.getResourceLocation(), clazz.getSimpleName());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
try {
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
@@ -234,7 +237,7 @@ public class StorageManager implements IStorageManager {
// 通配符无法匹配根目录,所以如果找不到,再从根目录查找一遍
if (CollectionUtils.isEmpty(resourceList)) {
packageSearchPath = StringUtils.format("{}/{}.{}", storageConfig.getResourceLocation(), clazz.getSimpleName(), storageConfig.getResourceSuffix());
packageSearchPath = StringUtils.format("{}/{}.*", storageConfig.getResourceLocation(), clazz.getSimpleName());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
}
@@ -25,8 +25,6 @@ public class StorageConfig {
private String resourceLocation;
private String resourceSuffix;
public String getId() {
return id;
}
@@ -50,12 +48,4 @@ public class StorageConfig {
public void setResourceLocation(String resourceLocation) {
this.resourceLocation = resourceLocation;
}
public String getResourceSuffix() {
return resourceSuffix;
}
public void setResourceSuffix(String resourceSuffix) {
this.resourceSuffix = resourceSuffix;
}
}
@@ -44,14 +44,14 @@ public class Storage<K, V> {
public Storage() {
}
public void init(InputStream inputStream, Class<?> resourceClazz) {
public void init(InputStream inputStream, Class<?> resourceClazz, String suffix) {
try {
this.clazz = (Class<V>) resourceClazz;
var reader = StorageContext.getResourceReader();
idDef = IdDef.valueOf(resourceClazz);
indexDefMap = IndexDef.createResourceIndexes(resourceClazz);
var list = reader.read(inputStream, resourceClazz);
var list = reader.read(inputStream, resourceClazz, suffix);
dataMap.clear();
indexMap.clear();
@@ -16,8 +16,7 @@ package com.zfoo.storage.schema;
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.interpreter.ResourceReader;
import com.zfoo.storage.manager.StorageManager;
import com.zfoo.storage.model.config.StorageConfig;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -70,20 +69,9 @@ public class StorageDefinitionParser implements BeanDefinitionParser {
resolvePlaceholder("id", "id", builder, element, parserContext);
resolvePlaceholder("package", "scanPackage", builder, scanElement, parserContext);
resolvePlaceholder("location", "resourceLocation", builder, resourceElement, parserContext);
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) {
@@ -99,11 +87,11 @@ public class StorageDefinitionParser implements BeanDefinitionParser {
builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
registry.registerBeanDefinition(name, builder.getBeanDefinition());
// 注册ExcelResourceReader
// clazz = ExcelResourceReader.class;
// name = StringUtils.uncapitalize(clazz.getName());
// builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
// registry.registerBeanDefinition(name, builder.getBeanDefinition());
// 注册ResourceReader
clazz = ResourceReader.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) {
@@ -0,0 +1,159 @@
/*
* 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.exception.RunException;
import com.zfoo.protocol.util.JsonUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.interpreter.ResourceConfig;
import com.zfoo.storage.interpreter.ResourceConfig.Header;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import org.apache.commons.io.FileUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
/**
* @author meiwei666
* @version 3.0
*/
public class ExcelToJsonUtils {
public static void excelConvertJson(String inputDir, String outputDir) throws Exception{
var listFiles = FileUtils.listFiles(new File(inputDir), new String[] { "xls", "xlsx"}, true);
for (var file : listFiles) {
var fileName = getFileName(file);
var inputStream = FileUtils.openInputStream(file);
var jsonStr = read(inputStream, fileName);
writeJsonFile(outputDir, jsonStr, fileName);
}
}
private static void writeJsonFile(String outDir, String jsonStr, String name) {
System.out.println("resource: " + name + ".txt");
PrintWriter pw = null;
try {
File outFile = new File(outDir, name + ".txt");
if (!outFile.exists())
outFile.createNewFile();
pw = new PrintWriter(outFile, "utf-8");
pw.write(jsonStr);
} catch (IOException e) {
System.err.println("error resource:" + name);
} finally {
if (pw != null)
pw.close();
}
}
private static String getFileName(File file) {
String name = file.getName();
int index = name.lastIndexOf(".");
if (index <= 0) {
return "";
}
return name.substring(0, index);
}
public static String read(InputStream inputStream, String fileName) {
var wb = createWorkbook(inputStream, fileName);
var resource = new ResourceConfig();
resource.setName(fileName);
// 默认取到第一个sheet页
var sheet = wb.getSheetAt(0);
//设置所有列
var headers = getHeaders(sheet, fileName);
resource.setHeader(headers);
// 行数定位到有效数据行,默认是第四行为有效数据行
var iterator = sheet.iterator();
iterator.next();
iterator.next();
iterator.next();
// 从ROW_SERVER这行开始读取数据
List<List<String>> data = new ArrayList<>();
while (iterator.hasNext()) {
var row = iterator.next();
List<String> rowData = new ArrayList<>();
for (var header : headers) {
var cell = row.getCell(header.getIndex());
var content = CellUtils.getCellStringValue(cell);
rowData.add(content);
}
data.add(rowData);
}
resource.setData(data);
return JsonUtils.object2StringPrettyPrinter(resource);
}
// 只读取代码里写的字段
private static List<Header> getHeaders(Sheet sheet, String fileName) {
var iterator = sheet.iterator();
// 获取配置表的有效列名称,默认第一行就是字段名称
var fieldRow = iterator.next();
if (fieldRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的属性控制列", fileName);
}
//默认第二行字段类型
var typeRow = iterator.next();
if (typeRow == null) {
throw new RunException("无法获取资源[class:{}]的Excel文件的类型控制列", fileName);
}
var headerList = new ArrayList<Header>();
var cellFieldMap = new HashMap<String, Integer>();
for (var i = 0; i < fieldRow.getLastCellNum(); i++) {
var fieldCell = fieldRow.getCell(i);
if (Objects.isNull(fieldCell)) {
continue;
}
var typeCell = typeRow.getCell(i);
if (Objects.isNull(typeCell)) {
continue;
}
var fieldName = CellUtils.getCellStringValue(fieldCell);
if (StringUtils.isEmpty(fieldName)) {
continue;
}
var typeName = CellUtils.getCellStringValue(typeCell);
if (StringUtils.isEmpty(typeName)) {
continue;
}
var previousValue = cellFieldMap.put(fieldName, i);
if (Objects.nonNull(previousValue)) {
throw new RunException("资源[class:{}]的Excel文件出现重复的属性控制列[field:{}]", fileName, fieldName);
}
headerList.add(new Header(fieldName, typeName, i));
}
return headerList;
}
private static Workbook createWorkbook(InputStream input, String fileName) {
try {
return WorkbookFactory.create(input);
} catch (IOException e) {
throw new RunException("静态资源[{}]异常,无法读取文件", fileName);
}
}
}
@@ -45,8 +45,6 @@
<xsd:complexType name="resource">
<!-- 本地资源路径 -->
<xsd:attribute name="location" type="xsd:string" use="required"/>
<!-- 资源文件后缀 -->
<xsd:attribute name="suffix" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="configType">
@@ -48,13 +48,14 @@ public class ApplicationTest {
var studentManager = context.getBean(StudentManager.class);
var studentResources = studentManager.studentResources;
// 类名称和Excel名称必须完全一致,Excel的列名称必须对应对象的属性名称
// 类名称和Excel名称必须完全一致,Excel的列名称必须对应对象的属性名称
for (StudentResource resource : studentResources.getAll()) {
logger.info(JsonUtils.object2String(resource));
}
System.out.println(StringUtils.MULTIPLE_HYPHENS);
// 通过id找到对应的行
var id = 1000;
var id = 1002;
var valueById = studentResources.get(id);
logger.info(JsonUtils.object2String(valueById));
System.out.println(StringUtils.MULTIPLE_HYPHENS);
@@ -62,27 +63,20 @@ 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<Integer, String> map = resource.getType9();
logger.info(map.get(1));
// logger.info(JsonUtils.object2String(resource));
System.err.println(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);
id = 2;
var resource = testResources.get(id);
logger.info(JsonUtils.object2String(resource));
System.out.println(StringUtils.MULTIPLE_HYPHENS);
}
}
@@ -22,7 +22,7 @@ import org.springframework.stereotype.Component;
* @author jaysunxiao
* @version 3.0
*/
//@Component
@Component
public class StudentManager {
@ResInjection
@@ -75,7 +75,8 @@ public class ConversionTest {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map = (Map<Integer, Integer>) conversionService.convert(str, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(map.getClass()));
// 注意:第3个参数不能写成TypeDescriptor.valueOf(map.getClass()) 而是要明确指定Map的key和value的类型
map = (Map<Integer, Integer>) conversionService.convert(str, TypeDescriptor.valueOf(String.class), TypeDescriptor.map(map.getClass(), TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
Assert.assertEquals(3, map.size());
}
@@ -13,6 +13,7 @@
package com.zfoo.storage.excel;
import com.zfoo.storage.util.ExcelToJsonUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -32,6 +33,13 @@ import java.util.Iterator;
@Ignore
public class ExcelTest {
@Test
public void excelConvertJson() throws Exception{
String inputDir = "E:\\workspace\\zfoo\\storage\\src\\test\\resources\\excel";
String outputDir = "E:\\workspace\\zfoo\\storage\\src\\test\\resources\\excel";
ExcelToJsonUtils.excelConvertJson(inputDir, outputDir);
}
@Test
public void createExcelTest() throws IOException {
//第一步创建workbook
@@ -21,7 +21,7 @@ import com.zfoo.storage.model.anno.Resource;
* @author jaysunxiao
* @version 3.0
*/
//@Resource
@Resource
public class StudentResource {
@Id
Binary file not shown.
+2 -3
View File
@@ -49,9 +49,8 @@
<storage:storage id="resourceManager">
<storage:scan package="com.zfoo.**.resource"/>
<!-- 如果是类路径一classpath开头,如果是其它目录则以file开头-->
<storage:resource location="classpath:/excel" suffix="txt"/>
<!--<storage:resource location="classpath:/excel" suffix="xlsx"/>-->
<!-- <storage:resource location="file:C:\Users\jaysunxiao\Desktop\excel" suffix="xlsx"/>-->
<storage:resource location="classpath:/excel"/>
<!-- <storage:resource location="file:C:\Users\jaysunxiao\Desktop\excel"/>-->
</storage:storage>
</beans>
@@ -1,45 +1,55 @@
{
"name": "Test",
"columns": [
"header": [
{
"name": "Id",
"type": "int"
"type": "int",
"index" : 0
},
{
"name": "Type0",
"type": "long"
"type": "long",
"index" : 0
},
{
"name": "Type1",
"type": "string"
"type": "string",
"index" : 0
},
{
"name": "Type2",
"type": "list\u003cstring\u003e"
"type": "list\u003cstring\u003e",
"index" : 0
},
{
"name": "Type3",
"type": "list\u003cint\u003e"
"type": "list\u003cint\u003e",
"index" : 0
},
{
"name": "Type4",
"type": "list\u003clong\u003e"
"type": "list\u003clong\u003e",
"index" : 0
},
{
"name": "Type5",
"type": "array\u003cattr,id,name\u003e"
"type": "array\u003cattr,id,name\u003e",
"index" : 0
},
{
"name": "Type8",
"type": "map\u003cint,int\u003e"
"type": "map\u003cint,int\u003e",
"index" : 0
},
{
"name": "Type9",
"type": "map\u003cint,string\u003e"
"type": "map\u003cint,string\u003e",
"index" : 0
},
{
"name": "Type10",
"type": "map\u003cstring,string\u003e"
"type": "map\u003cstring,string\u003e",
"index" : 0
}
],
"data": [
@@ -20,6 +20,7 @@ import io.netty.util.concurrent.FastThreadLocal;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
@@ -29,7 +30,7 @@ import java.util.Base64;
* <p>
* 默认AES/ECB/PKCS5Padding
*
* @author jaysunxiao
* @author godotg
* @version 3.0
*/
public abstract class AesUtils {
@@ -46,13 +47,6 @@ public abstract class AesUtils {
*/
private static final String ALGORITHM_STR = "AES/ECB/PKCS5Padding";
private static final FastThreadLocal<Cipher> LOCAL_CIPHER = new FastThreadLocal<Cipher>() {
@Override
protected Cipher initialValue() throws NoSuchPaddingException, NoSuchAlgorithmException {
return Cipher.getInstance(ALGORITHM_STR);
}
};
static {
try {
KEY = new SecretKeySpec(KEY_STR.getBytes(StringUtils.DEFAULT_CHARSET_NAME), ALGORITHM);
@@ -61,6 +55,24 @@ public abstract class AesUtils {
}
}
private static final FastThreadLocal<Cipher> LOCAL_ENCRYPT_CIPHER = new FastThreadLocal<Cipher>() {
@Override
protected Cipher initialValue() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
var cipher = Cipher.getInstance(ALGORITHM_STR);
cipher.init(Cipher.ENCRYPT_MODE, KEY);
return cipher;
}
};
private static final FastThreadLocal<Cipher> LOCAL_DECRYPT_CIPHER = new FastThreadLocal<Cipher>() {
@Override
protected Cipher initialValue() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
var cipher = Cipher.getInstance(ALGORITHM_STR);
cipher.init(Cipher.DECRYPT_MODE, KEY);
return cipher;
}
};
/**
* 对str进行AES加密
@@ -81,9 +93,7 @@ public abstract class AesUtils {
public static byte[] encrypt(byte[] bytes) {
try {
var cipher = LOCAL_CIPHER.get();
cipher.init(Cipher.ENCRYPT_MODE, KEY);
return cipher.doFinal(bytes);
return LOCAL_ENCRYPT_CIPHER.get().doFinal(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -108,9 +118,7 @@ public abstract class AesUtils {
public static byte[] decrypt(byte[] bytes) {
try {
var cipher = LOCAL_CIPHER.get();
cipher.init(Cipher.DECRYPT_MODE, KEY);
return cipher.doFinal(bytes);
return LOCAL_DECRYPT_CIPHER.get().doFinal(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -23,9 +23,11 @@ public class AesUtilsTest {
@Test
public void test() {
String passWord = "hello world";
String encodePassWorld = AesUtils.getEncryptString(passWord);
Assert.assertEquals(passWord, AesUtils.getDecryptString(encodePassWorld));
for (int i = 0; i < 10; i++) {
var passWord = "hello world";
var encodePassWorld = AesUtils.getEncryptString(passWord);
Assert.assertEquals(passWord, AesUtils.getDecryptString(encodePassWorld));
}
}
}