init project

This commit is contained in:
jaysunxiao committed 2021-05-20 14:17:21 +08:00
1 parent b7f28da485
commit bce060a28e
573 files changed
+84513

No files matched your search

@@ -0,0 +1,110 @@
/*
* 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.protocol.util.StringUtils;
import com.zfoo.storage.interpreter.IResourceReader;
import com.zfoo.storage.manager.IStorageManager;
import com.zfoo.storage.schema.ResInjectionProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.Ordered;
import org.springframework.core.convert.ConversionService;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StorageContext implements ApplicationListener<ApplicationContextEvent>, Ordered {
private static StorageContext instance;
private ApplicationContext applicationContext;
private IResourceReader resourceReader;
private IStorageManager storageManager;
private ConversionService conversionService;
public static StorageContext getStorageContext() {
return instance;
}
public static ApplicationContext getApplicationContext() {
return instance.applicationContext;
}
public static StorageContext getInstance() {
return instance;
}
public static ConversionService getConversionService() {
return instance.conversionService;
}
public static IResourceReader getResourceReader() {
return instance.resourceReader;
}
public static IStorageManager getStorageManager() {
return instance.storageManager;
}
public static void injectResource() {
var applicationContext = instance.applicationContext;
var beanNames = applicationContext.getBeanDefinitionNames();
var processor = applicationContext.getBean(ResInjectionProcessor.class);
for (var beanName : beanNames) {
processor.postProcessAfterInitialization(applicationContext.getBean(beanName), beanName);
}
}
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
if (instance != null) {
return;
}
// 初始化上下文
StorageContext.instance = this;
instance.applicationContext = event.getApplicationContext();
instance.conversionService = (ConversionService) applicationContext.getBean(StringUtils.uncapitalize(ConversionServiceFactoryBean.class.getName()));
instance.resourceReader = applicationContext.getBean(IResourceReader.class);
instance.storageManager = applicationContext.getBean(IStorageManager.class);
// 初始化,并读取配置表
instance.storageManager.initBefore();
// 注入配置表资源
injectResource();
// 移除没有被引用的不必要资源,为了节省服务器内存
instance.storageManager.initAfter();
} else if (event instanceof ContextClosedEvent) {
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
@@ -0,0 +1,168 @@
/*
* 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.interpreter;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
import com.zfoo.storage.model.anno.Id;
import com.zfoo.storage.util.CellUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
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.util.*;
import java.util.stream.Collectors;
/**
* @author jaysunxiao
* @version 3.0
*/
public class ExcelResourceReader implements IResourceReader {
private static final TypeDescriptor TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
@Override
public <T> List<T> read(InputStream inputStream, Class<T> clazz) {
var wb = createWorkbook(inputStream, clazz);
var result = new ArrayList<T>();
// 默认取到第一个sheet页
var sheet = wb.getSheetAt(0);
var fieldInfos = getFieldInfos(sheet, clazz);
var iterator = sheet.iterator();
// 行数定位到有效数据行,默认是第四行为有效数据行
iterator.next();
iterator.next();
iterator.next();
// 从ROW_SERVER这行开始读取数据
while (iterator.hasNext()) {
var row = iterator.next();
var instance = ReflectionUtils.newInstance(clazz);
var idCell = row.getCell(0);
if (idCell == null || StringUtils.isBlank(CellUtils.getCellStringValue(idCell))) {
continue;
}
for (var fieldInfo : fieldInfos) {
var cell = row.getCell(fieldInfo.index);
if (cell != null) {
var content = CellUtils.getCellStringValue(cell);
if (!StringUtils.isEmpty(content)) {
inject(instance, fieldInfo.field, content);
}
}
// 如果读的是id列的单元格,则判断当前id是否为空
if (fieldInfo.field.isAnnotationPresent(Id.class)) {
if (cell == null || StringUtils.isEmpty(CellUtils.getCellStringValue(cell))) {
throw new RuntimeException(StringUtils.format("静态资源[resource:{}]存在id未配置的项", clazz.getSimpleName()));
}
}
}
result.add(instance);
}
return result;
}
private void inject(Object instance, Field field, String content) {
try {
var targetType = new TypeDescriptor(field);
var value = StorageContext.getConversionService().convert(content, TYPE_DESCRIPTOR, targetType);
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, instance, value);
} catch (Exception e) {
throw new RuntimeException(StringUtils.format("无法将Excel资源[class:{}]中的[content:{}]转换为属性[field:{}]"
, instance.getClass().getSimpleName(), content, field.getName()), e);
}
}
// 只读取代码里写的字段
private Collection<FieldInfo> getFieldInfos(Sheet sheet, Class<?> clazz) {
var fieldRow = getFieldRow(sheet);
if (fieldRow == null) {
throw new RuntimeException(StringUtils.format("无法获取资源[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 RuntimeException(StringUtils.format("资源[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 RuntimeException(StringUtils.format("资源类[class:{}]的声明属性[filed:{}]无法获取,请检查配置表的格式", clazz, field.getName()));
}
}
return fieldList.stream().map(it -> new FieldInfo(cellFieldMap.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) {
try {
return WorkbookFactory.create(input);
} catch (IOException e) {
throw new RuntimeException(StringUtils.format("静态资源[{}]异常,无法读取文件", 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;
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.interpreter;
import java.io.InputStream;
import java.util.List;
/**
* 解释器模式设计的文本解析器
* <p>
* interpreter [in'ter·pret·er || ɪn'tɜrprɪtə(r) /-'tɜːp-]
* n. 直译程序, 翻译员, 解释者
*
* @author jaysunxiao
* @version 3.0
*/
public interface IResourceReader {
<T> List<T> read(InputStream inputStream, Class<T> clazz);
}
@@ -0,0 +1,47 @@
/*
* 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.manager;
import com.zfoo.storage.model.vo.Storage;
import org.springframework.lang.Nullable;
import java.util.Map;
import java.util.Set;
/**
* @author jaysunxiao
* @version 3.0
*/
public interface IStorageManager {
/**
* 配置表初始化之前,先读取所有的excel
*/
void initBefore();
/**
* 程序加载过后,移除没有用到的配置表
*/
void initAfter();
@Nullable
Storage<?, ?> getStorage(Class<?> clazz);
Set<Class<?>> allStorageClassSet();
Map<Class<?>, Storage<?, ?>> getStorageMap();
void updateStorage(Class<?> clazz, Storage<?, ?> storage);
}
@@ -0,0 +1,192 @@
/*
* 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.manager;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.exception.ExceptionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.model.config.StorageConfig;
import com.zfoo.storage.model.vo.ResourceDef;
import com.zfoo.storage.model.vo.Storage;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.util.ResourceUtils;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StorageManager implements IStorageManager {
// ANT通配符有三种, ? :匹配任何单字符; * :匹配0或者任意数量的字符; ** :匹配0或者更多的目录
// 1. /project/*.a 匹配项目根路径下所有在project路径下的.a文件
// 2. /project/p?ttern 匹配项目根路径下 /project/pattern 和 /app/pXttern,但是不包括/app/pttern
// 3. /**/example 匹配项目根路径下 /project/example, /project/foow/example, 和 /example
// 4. /project/**/dir/file.* 匹配项目根路径下/project/dir/file.jsp, /project/foow/dir/file.html,/project/foow/bar/dir/file.pdf
// 5. /**/*.jsp 匹配项目根路径下任何的.jsp 文件
// classpath:和classpath*: 的区别,前者只会第一个加载到的类,后者会加载所有的类,包括jar文件下的类
private static final String SUFFIX_PATTERN = "**/*.class";
private StorageConfig storageConfig;
/**
* 在当前项目被依赖注入,被使用的Storage
*/
private Map<Class<?>, Storage<?, ?>> storageMap = new HashMap<>();
/**
* 全部的Storage定义
*/
private Set<Class<?>> allStorageClassSet = new HashSet<>();
public StorageConfig getStorageConfig() {
return storageConfig;
}
public void setStorageConfig(StorageConfig storageConfig) {
this.storageConfig = storageConfig;
}
@Override
public void initBefore() {
var resourceDefinitionMap = new HashMap<Class<?>, ResourceDef>();
var clazzNameSet = scanResourceAnno(storageConfig.getScanPackage());
for (var clazzName : clazzNameSet) {
Class<?> resourceClazz;
try {
resourceClazz = Class.forName(clazzName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(StringUtils.format("无法获取资源类[{}]", clazzName));
}
var resourceFile = scanResourceFile(resourceClazz);
ResourceDef resourceDef = new ResourceDef(resourceClazz, resourceFile);
if (resourceDefinitionMap.containsKey(resourceClazz)) {
throw new RuntimeException(StringUtils.format("类的资源定义[{}]已经存在[{}]", resourceClazz, resourceDef));
}
resourceDefinitionMap.put(resourceClazz, resourceDef);
}
try {
for (var definition : resourceDefinitionMap.values()) {
var clazz = definition.getClazz();
Storage<?, ?> storage = new Storage<>();
storage.init(definition.getResource().getInputStream(), definition.getClazz());
storageMap.putIfAbsent(clazz, storage);
allStorageClassSet.add(clazz);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void initAfter() {
var unusableStorageClassList = storageMap.entrySet().stream()
.filter(it -> !it.getValue().isUsable())
.map(it -> it.getKey())
.collect(Collectors.toList());
unusableStorageClassList.forEach(it -> {
storageMap.remove(it);
});
}
@Override
public Storage<?, ?> getStorage(Class<?> clazz) {
return storageMap.get(clazz);
}
@Override
public Set<Class<?>> allStorageClassSet() {
return allStorageClassSet;
}
@Override
public Map<Class<?>, Storage<?, ?>> getStorageMap() {
return storageMap;
}
@Override
public void updateStorage(Class<?> clazz, Storage<?, ?> storage) {
storageMap.put(clazz, storage);
}
private Set<String> scanResourceAnno(String scanLocation) {
var resourcePatternResolver = new PathMatchingResourcePatternResolver();
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
try {
var packageSearchPath = ResourceUtils.CLASSPATH_URL_PREFIX + scanLocation.replace(StringUtils.PERIOD, StringUtils.SLASH) + StringUtils.SLASH + SUFFIX_PATTERN;
var resources = resourcePatternResolver.getResources(packageSearchPath);
var result = new HashSet<String>();
var name = com.zfoo.storage.model.anno.Resource.class.getName();
for (var resource : resources) {
if (resource.isReadable()) {
var metadataReader = metadataReaderFactory.getMetadataReader(resource);
var annoMeta = metadataReader.getAnnotationMetadata();
if (annoMeta.hasAnnotation(name)) {
ClassMetadata clazzMeta = metadataReader.getClassMetadata();
result.add(clazzMeta.getClassName());
}
}
}
return result;
} catch (IOException e) {
throw new RuntimeException("无法读取资源信息:" + e);
}
}
private Resource scanResourceFile(Class<?> clazz) {
var resourcePatternResolver = new PathMatchingResourcePatternResolver();
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
try {
var resourceList = new ArrayList<Resource>();
var packageSearchPath = StringUtils.format("{}/**/{}.{}", storageConfig.getResourceLocation(), clazz.getSimpleName(), storageConfig.getResourceSuffix());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
try {
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
} catch (Exception e) {
}
// 通配符无法匹配根目录,所以如果找不到,再从根目录查找一遍
if (CollectionUtils.isEmpty(resourceList)) {
packageSearchPath = StringUtils.format("{}/{}.{}", storageConfig.getResourceLocation(), clazz.getSimpleName(), storageConfig.getResourceSuffix());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
}
if (CollectionUtils.isEmpty(resourceList)) {
throw new RuntimeException(StringUtils.format("无法找到配置文件[{}]", clazz.getSimpleName()));
} else if (resourceList.size() > 1) {
throw new RuntimeException(StringUtils.format("找到重复的配置文件[{}]", clazz.getSimpleName()));
} else {
return resourceList.get(0);
}
} catch (IOException e) {
throw new RuntimeException(ExceptionUtils.getMessage(e));
}
}
}
@@ -0,0 +1,30 @@
/*
* 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.model.anno;
import java.lang.annotation.*;
/**
* 主键
*
* @author jaysunxiao
* @version 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Id {
}
@@ -0,0 +1,32 @@
/*
* 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.model.anno;
import java.lang.annotation.*;
/**
* 索引,索引的名称使用字段属性的名称,用HaspMap实现
*
* @author jaysunxiao
* @version 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Index {
boolean unique() default false;
}
@@ -0,0 +1,34 @@
/*
* 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.model.anno;
import java.lang.annotation.*;
/**
* 静态数据的注入
*
* @author jaysunxiao
* @version 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ResInjection {
String value() default "";
boolean unique() default false;
}
@@ -0,0 +1,28 @@
/*
* 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.model.anno;
import java.lang.annotation.*;
/**
* 资源注解
*
* @author jaysunxiao
* @version 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Resource {
}
@@ -0,0 +1,61 @@
/*
* 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.model.config;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StorageConfig {
private String id;
private String scanPackage;
private String resourceLocation;
private String resourceSuffix;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getScanPackage() {
return scanPackage;
}
public void setScanPackage(String scanPackage) {
this.scanPackage = scanPackage;
}
public String getResourceLocation() {
return resourceLocation;
}
public void setResourceLocation(String resourceLocation) {
this.resourceLocation = resourceLocation;
}
public String getResourceSuffix() {
return resourceSuffix;
}
public void setResourceSuffix(String resourceSuffix) {
this.resourceSuffix = resourceSuffix;
}
}
@@ -0,0 +1,56 @@
/*
* 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.model.vo;
import com.zfoo.protocol.exception.RunException;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.storage.model.anno.Id;
import java.lang.reflect.Field;
/**
* @author jaysunxiao
* @version 3.0
*/
public class IdDef {
private Field field;
public static IdDef valueOf(Class<?> clazz) {
Field[] fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Id.class);
if (fields.length <= 0) {
throw new RunException("class[{}]没有主键Id注解", clazz.getName());
}
if (fields.length > 1) {
throw new RunException("类[{}]的主键Id注解重复", clazz.getName());
}
if (fields[0] == null) {
throw new RunException("不合法的Id资源映射对象:" + clazz.getName());
}
Field idField = fields[0];
ReflectionUtils.makeAccessible(idField);
IdDef idDef = new IdDef();
idDef.setField(idField);
return idDef;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
}
@@ -0,0 +1,79 @@
/*
* 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.model.vo;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.model.anno.Index;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 简化索引的名称,使用字段的名称作为索引的名称
*
* @author jaysunxiao
* @version 3.0
*/
public class IndexDef {
private boolean unique;
private Field field;
public IndexDef(Field field) {
ReflectionUtils.makeAccessible(field);
this.field = field;
var index = field.getAnnotation(Index.class);
this.unique = index.unique();
}
public static Map<String, IndexDef> createResourceIndexes(Class<?> clazz) {
var fields = ReflectionUtils.getFieldsByAnnoInPOJOClass(clazz, Index.class);
var indexes = new ArrayList<IndexDef>(fields.length);
for (var field : fields) {
IndexDef indexDef = new IndexDef(field);
indexes.add(indexDef);
}
var result = new HashMap<String, IndexDef>();
for (var index : indexes) {
var indexName = index.field.getName();
if (result.put(indexName, index) != null) {
throw new RuntimeException(StringUtils.format("资源类[{}]索引名称重复,索引名[}|]", clazz.getName(), indexName));
}
}
return result;
}
public boolean isUnique() {
return unique;
}
public void setUnique(boolean unique) {
this.unique = unique;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
}
@@ -0,0 +1,41 @@
/*
* 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.model.vo;
import org.springframework.core.io.Resource;
/**
* @author jaysunxiao
* @version 3.0
*/
public class ResourceDef {
private final Class<?> clazz;
private final Resource resource;
public ResourceDef(Class<?> clazz, Resource resource) {
this.clazz = clazz;
this.resource = resource;
}
public Class<?> getClazz() {
return clazz;
}
public Resource getResource() {
return resource;
}
}
@@ -0,0 +1,150 @@
/*
* 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.model.vo;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.util.AssertionUtils;
import com.zfoo.protocol.util.IOUtils;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
import org.springframework.lang.Nullable;
import java.io.InputStream;
import java.util.*;
/**
* @author jaysunxiao
* @version 3.0
*/
public class Storage<K, V> {
private Class<V> clazz;
private final Map<K, V> dataMap = new HashMap<>();
private final Map<String, Map<Object, List<V>>> indexMap = new HashMap<>();
private final Map<String, Map<Object, V>> uniqueIndexMap = new HashMap<>();
private IdDef idDef;
private Map<String, IndexDef> indexDefMap;
/**
* 是否被使用
*/
private boolean usable = false;
public Storage() {
}
public void init(InputStream inputStream, Class<?> resourceClazz) {
try {
this.clazz = (Class<V>) resourceClazz;
var reader = StorageContext.getResourceReader();
idDef = IdDef.valueOf(resourceClazz);
indexDefMap = IndexDef.createResourceIndexes(resourceClazz);
var list = reader.read(inputStream, resourceClazz);
dataMap.clear();
indexMap.clear();
uniqueIndexMap.clear();
for (var object : list) {
put((V) object);
}
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
IOUtils.closeIO(inputStream);
}
}
public Collection<V> getAll() {
return Collections.unmodifiableCollection(dataMap.values());
}
public boolean contain(K key) {
return dataMap.containsKey(key);
}
public V get(K id) {
V result = dataMap.get(id);
AssertionUtils.notNull(result, "静态资源[resource:{}]种表示为[id:{}]的静态资源不存在", clazz.getSimpleName(), id);
return (V) result;
}
public List<V> getIndex(String indexName, Object key) {
var indexValues = indexMap.get(indexName);
AssertionUtils.notNull(indexValues, "静态资源[resource:{}]不存在为[indexName:{}]的索引", clazz.getSimpleName(), indexName);
var values = indexValues.get(key);
if (CollectionUtils.isEmpty(values)) {
return Collections.emptyList();
}
return values;
}
@Nullable
public V getUniqueIndex(String uniqueIndexName, Object key) {
var indexValueMap = uniqueIndexMap.get(uniqueIndexName);
AssertionUtils.notNull(indexValueMap, "静态资源[resource:{}]不存在为[uniqueIndexName:{}]的唯一索引", clazz.getSimpleName(), uniqueIndexName);
var value = indexValueMap.get(key);
return value;
}
private V put(V value) {
var key = (K) ReflectionUtils.getField(idDef.getField(), value);
if (key == null) {
throw new RuntimeException("静态资源存在id未配置的项");
}
if (dataMap.containsKey(key)) {
throw new RuntimeException(StringUtils.format("静态资源[resource:{}]的[id:{}]重复", clazz.getSimpleName(), key));
}
// 添加资源
var result = dataMap.put(key, value);
// 添加索引
for (var def : indexDefMap.values()) {
// 使用field的名称作为索引的名称
var indexKey = def.getField().getName();
var indexValue = ReflectionUtils.getField(def.getField(), value);
if (def.isUnique()) {// 唯一索引
var index = uniqueIndexMap.computeIfAbsent(indexKey, k -> new HashMap<>());
if (index.put(indexValue, value) != null) {
throw new RuntimeException(StringUtils.format("静态资源[class:{}]的唯一索引重复[index:{}][value:{}]", clazz.getName(), indexKey, indexValue));
}
} else {// 不是唯一索引
var index = indexMap.computeIfAbsent(indexKey, k -> new HashMap<>());
var list = index.computeIfAbsent(indexValue, k -> new ArrayList<V>());
list.add(value);
}
}
return result;
}
public int size() {
return dataMap.size();
}
public boolean isUsable() {
return usable;
}
public void setUsable(boolean usable) {
this.usable = usable;
}
}
@@ -0,0 +1,28 @@
/*
* 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.schema;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* @author jaysunxiao
* @version 3.0
*/
public class NamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser(StorageDefinitionParser.STORAGE, new StorageDefinitionParser());
}
}
@@ -0,0 +1,94 @@
/*
* 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.schema;
import com.zfoo.protocol.util.ReflectionUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
import com.zfoo.storage.model.anno.Id;
import com.zfoo.storage.model.anno.ResInjection;
import com.zfoo.storage.model.vo.Storage;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.Consumer;
/**
* Bean级生命周期接口和容器生命周期接口是个性和共性的辩证统一思想的体现,
* 前者解决Bean个性化处理的问题,而后者解决容器中某些Bean共性处理的问题
*
* @author jaysunxiao
* @version 3.0
*/
public class ResInjectionProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (StorageContext.getStorageContext() == null) {
return bean;
}
ReflectionUtils.filterFieldsInClass(bean.getClass(), null, new Consumer<Field>() {
@Override
public void accept(Field field) {
ResInjection anno = field.getAnnotation(ResInjection.class);
if (anno != null) {
injectStorage(bean, field, anno);
}
}
});
return bean;
}
private void injectStorage(Object bean, Field field, ResInjection anno) {
Type type = field.getGenericType();
if (!(type instanceof ParameterizedType)) {
throw new RuntimeException(StringUtils.format("[bean:{}]类型声明不正确,不是泛型类", bean.getClass().getSimpleName()));
}
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
// @ResInjection
// Storage<Integer, ActivityResource> resources;
Class<?> keyClazz = (Class<?>) types[0];
Class<?> resourceClazz = (Class<?>) types[1];
Storage<?, ?> storage = StorageContext.getStorageManager().getStorage(resourceClazz);
if (storage == null) {
throw new RuntimeException(StringUtils.format("静态类资源[resource:{}]不存在", resourceClazz.getSimpleName()));
}
Field[] idFields = ReflectionUtils.getFieldsByAnnoInPOJOClass(resourceClazz, Id.class);
if (idFields.length != 1) {
throw new RuntimeException(StringUtils.format("静态类资源[resource:{}]配置没有注解id", resourceClazz.getSimpleName()));
}
if (!keyClazz.getSimpleName().toLowerCase().contains(idFields[0].getType().getSimpleName().toLowerCase())) {
throw new RuntimeException(StringUtils.format("静态类资源[resource:{}]配置注解[id:{}]类型和泛型类型[type:{}]不匹配"
, resourceClazz.getSimpleName(), idFields[0].getType().getSimpleName(), keyClazz.getSimpleName()));
}
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, storage);
storage.setUsable(true);
}
}
@@ -0,0 +1,128 @@
/*
* 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.schema;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
import com.zfoo.storage.interpreter.ExcelResourceReader;
import com.zfoo.storage.manager.StorageManager;
import com.zfoo.storage.model.config.StorageConfig;
import com.zfoo.storage.strategy.*;
import com.zfoo.util.DomUtils;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.w3c.dom.Element;
import java.util.ArrayList;
import java.util.List;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StorageDefinitionParser implements BeanDefinitionParser {
// 配置标签名称
public static final String STORAGE = "storage";
@Override
public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
// 注册StorageConfig
parseStorageConfig(element, parserContext);
// 注入StorageSpringContext,ExcelResourceReader,ResInjectionProcessor,ConversionService
registerBeanDefinition(parserContext);
// 注入StorageManager
var clazz = StorageManager.class;
var name = StringUtils.uncapitalize(clazz.getName());
var builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
builder.addPropertyReference("storageConfig", StorageConfig.class.getCanonicalName());
parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition());
return builder.getBeanDefinition();
}
private void parseStorageConfig(Element element, ParserContext parserContext) {
var clazz = StorageConfig.class;
var builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
var scanElement = DomUtils.getFirstChildElementByTagName(element, "scan");
if (scanElement == null) {
throw new RuntimeException("XML文件缺少[scan]元素定义");
}
var resourceElement = DomUtils.getFirstChildElementByTagName(element, "resource");
if (resourceElement == null) {
throw new RuntimeException("XML文件缺少[resource]元素定义");
}
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());
}
private void registerBeanDefinition(ParserContext parserContext) {
var registry = parserContext.getRegistry();
Class<?> clazz;
String name;
BeanDefinitionBuilder builder;
// 注入StorageSpringContext
clazz = StorageContext.class;
name = StringUtils.uncapitalize(clazz.getName());
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());
// 注入StaticInjectProcessor
clazz = ResInjectionProcessor.class;
name = StringUtils.uncapitalize(clazz.getName());
builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
registry.registerBeanDefinition(name, builder.getBeanDefinition());
// 注入ConversionServiceFactoryBean
clazz = ConversionServiceFactoryBean.class;
name = StringUtils.uncapitalize(clazz.getName());
builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
List<Object> converters = new ArrayList<>();
converters.add(new JsonToArrayConverter());
converters.add(new JsonToMapConverter());
converters.add(new JsonToObjectConverter());
converters.add(new StringToClassConverter());
converters.add(new StringToDateConverter());
converters.add(new StringToMapConverter());
builder.addPropertyValue("converters", converters);
registry.registerBeanDefinition(name, builder.getBeanDefinition());
}
private void resolvePlaceholder(String attributeName, String fieldName, BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
var attributeValue = element.getAttribute(attributeName);
var environment = parserContext.getReaderContext().getEnvironment();
var placeholder = environment.resolvePlaceholders(attributeValue);
builder.addPropertyValue(fieldName, placeholder);
}
}
@@ -0,0 +1,64 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.JsonUtils;
import com.zfoo.protocol.util.StringUtils;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import java.util.Collections;
import java.util.Set;
/**
* @author jaysunxiao
* @version 3.0
*/
public class JsonToArrayConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType() == String.class && targetType.getType().isArray();
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
// String content = (String) source;
// return targetType.isPrimitive() ? JsonUtil.string2Object(content, targetType.getObjectType())
// : JsonUtil.string2Array(content, targetType.getType());
Class<?> clazz = null;
String content = (String) source;
String targetClazzName = targetType.getObjectType().getName();
if (targetClazzName.contains(StringUtils.LEFT_SQUARE_BRACKET) || targetClazzName.contains(StringUtils.SEMICOLON)) {
String clazzPath = targetClazzName.substring(2, targetClazzName.length() - 1);
try {
clazz = Class.forName(clazzPath);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else {
clazz = targetType.getObjectType();
}
return JsonUtils.string2Array(content, clazz);
}
}
@@ -0,0 +1,46 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.JsonUtils;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* @author jaysunxiao
* @version 3.0
*/
public class JsonToMapConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType() == String.class && Map.class.isAssignableFrom(targetType.getType());
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Map.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String content = (String) source;
return JsonUtils.string2Object(content, targetType.getType());
// return JsonUtil.string2Map(content, targetType.getMapKeyTypeDescriptor().getType()
// , targetType.getMapValueTypeDescriptor().getType());
}
}
@@ -0,0 +1,64 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.JsonUtils;
import com.zfoo.protocol.util.ReflectionUtils;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import java.util.Collections;
import java.util.Set;
/**
* 转换一个String到一个POJO对象,且这个对象不能继承如何接口
*
* @author jaysunxiao
* @version 3.0
*/
public class JsonToObjectConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.getType() != String.class) {
return false;
}
if (targetType.getType().isPrimitive()) {
return false;
}
if (Number.class.isAssignableFrom(targetType.getType())) {
return false;
}
if (CharSequence.class.isAssignableFrom(targetType.getType())) {
return false;
}
return ReflectionUtils.isPOJOClass(targetType.getType());
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Object.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String content = (String) source;
return JsonUtils.string2Object(content, targetType.getType());
}
}
@@ -0,0 +1,50 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.StorageContext;
import org.springframework.core.convert.converter.Converter;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StringToClassConverter implements Converter<String, Class<?>> {
@Override
public Class<?> convert(String source) {
if (!source.contains(".") && !source.startsWith("[")) {
source = "java.lang." + source;
}
ClassLoader loader = null;
StorageContext context = StorageContext.getInstance();
if (context != null) {
loader = StorageContext.getApplicationContext().getClassLoader();
} else {
loader = Thread.currentThread().getContextClassLoader();
}
try {
return Class.forName(source, true, loader);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(StringUtils.format("无法将字符串[{}]转换为Class对象", source));
}
}
}
@@ -0,0 +1,40 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.StringUtils;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return df.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException(StringUtils.format("字符串[{}]不符合格式要求:[yyyy-MM-dd HH:mm:ss]", source));
}
}
}
@@ -0,0 +1,30 @@
/*
* 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.strategy;
import com.zfoo.protocol.util.JsonUtils;
import org.springframework.core.convert.converter.Converter;
import java.util.Map;
/**
* @author jaysunxiao
* @version 3.0
*/
public class StringToMapConverter implements Converter<String, Map<String, Object>> {
@Override
public Map<String, Object> convert(String source) {
return JsonUtils.string2Map(source, String.class, Object.class);
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2020 The zfoo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
package com.zfoo.storage.util;
import com.zfoo.protocol.util.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaError;
/**
* @author jaysunxiao
* @version 3.0
*/
public abstract class CellUtils {
/**
* 获取单元格值
*
* @return 值,类型可能为:Date、Double、Boolean、String
*/
public static Object getCellValue(Cell cell) {
return getCellValue(cell, cell.getCellType());
}
public static String getCellStringValue(Cell cell) {
return getCellValue(cell).toString().trim();
}
/**
* 获取单元格值<br>
* 如果单元格值为数字格式,则判断其格式中是否有小数部分,无则返回Long类型,否则返回Double类型
*/
public static Object getCellValue(Cell cell, CellType cellType) {
Object value;
switch (cellType) {
case NUMERIC:
value = getNumericValue(cell);
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case FORMULA:
// 遇到公式时查找公式结果类型
value = getCellValue(cell, cell.getCachedFormulaResultType());
break;
case BLANK:
value = StringUtils.EMPTY;
break;
case ERROR:
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue());
value = (null == error) ? StringUtils.EMPTY : error.getString();
break;
default:
value = cell.getStringCellValue();
}
return value;
}
// -------------------------------------------------------------------------------------------------------------- Private method start
/**
* 获取数字类型的单元格值
*
* @return 单元格值,可能为Long、Double、Date
*/
private static Object getNumericValue(Cell cell) {
var value = cell.getNumericCellValue();
var style = cell.getCellStyle();
if (null == style) {
return value;
}
// 判断是否为日期
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue();
}
var format = style.getDataFormatString();
// 普通数字
if (null != format && !format.contains(StringUtils.PERIOD)) {
var longValue = (long) value;
if (longValue == value) {
// 对于无小数部分的数字类型,转为Long
return longValue;
}
}
return value;
}
}
@@ -0,0 +1 @@
http\://www.zfoo.com/schema/storage=com.zfoo.storage.schema.NamespaceHandler
@@ -0,0 +1 @@
http\://www.zfoo.com/schema/storage-1.0.xsd=storage-1.0.xsd
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!--xsd文件本身是xml文件,第一行是xml声明-->
<!-- 第一行必须要,
version属性指定版本,
encoding属性编码格式,
standalone属性,如果是yes,则表示这个XML文档是独立的,不能引用外部的DTD规范文件;如果是no,则该XML文档不是独立的,表示可以用外部的DTD规范文档
XML的属性值须加引号 -->
<!--**************************************分割线**************************************************-->
<!--第一个xmlns表示schema中用到的元素和数据类型来自命名空间"http://www.w3.org/2001/XMLSchema"。
同时它还规定了来自命名空间 "http://www.w3.org/2001/XMLSchema" 的元素和数据类型应该使用前缀 xsd,可以为任何自定义名称:-->
<!--第二个xmlns表示引用spring中beans的命名空间-->
<!--xmlns="http://www.zfoo.com/schema/user" ,指出默认的命名空间是 "http://www.zfoo.com/schema/user" -->
<!--targetNamespace显示被此 schema 定义的元素 (note, to, from, heading, body) 来自命名空间:
http://www.zfoo.com/schema/user -->
<!--elementFormDefault指出任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定。 -->
<!--attributeFormDefault属性:
指定XML文档使用schema中定义的局部属性时是否必须使用命名空间限定
qualified:必须通过命名空间前缀限定unqualified:(默认值)无须通过命名空间前缀限定 -->
<!--**************************************分割线**************************************************-->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.zfoo.com/schema/storage"
targetNamespace="http://www.zfoo.com/schema/storage"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<!-- scan扫描包类型 -->
<!-- package声明类型 -->
<xsd:complexType name="scan">
<xsd:attribute name="package" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- 格式声明类型 -->
<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">
<xsd:sequence>
<xsd:element name="scan" type="scan"/>
<xsd:element name="resource" type="resource"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- 表示定义标签 -->
<xsd:element name="storage" type="configType"/>
</xsd:schema>
@@ -0,0 +1,64 @@
/*
* 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.protocol.util.JsonUtils;
import com.zfoo.protocol.util.StringUtils;
import com.zfoo.storage.resource.StudentResource;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author jaysunxiao
* @version 3.0
*/
@Ignore
public class ApplicationTest {
private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
// storage教程
@Test
public void startStorageTest() {
// 加载配置文件,配置文件中必须引入storage
// 配置文件中scan,需要映射Excel的类所在位置
// 配置文件中resource,需要映射Excel的文件所在位置
var context = new ClassPathXmlApplicationContext("application.xml");
// Excel的映射内容需要在被Spring管理的bean的方法上加上@ResInjection注解,即可自动注入Excel对应的对象
// 参考StudentManager中的标准用法
var studentManager = context.getBean(StudentManager.class);
var studentResources = studentManager.studentResources;
// 类名称和Excel名称必须完全一致,Excel的列名称必须对应对象的属性名称
for (StudentResource resource : studentResources.getAll()) {
logger.info(JsonUtils.object2String(resource));
}
System.out.println(StringUtils.MULTIPLE_HYPHENS);
// 通过id找到对应的行
var id = 1000;
var valueById = studentResources.get(id);
logger.info(JsonUtils.object2String(valueById));
System.out.println(StringUtils.MULTIPLE_HYPHENS);
// 通过索引找对应的行
var valuesByIndex = studentResources.getIndex("name", "james0");
logger.info(JsonUtils.object2String(valuesByIndex));
}
}
@@ -0,0 +1,31 @@
/*
* 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 org.springframework.stereotype.Component;
/**
* @author jaysunxiao
* @version 3.0
*/
@Component
public class StudentManager {
@ResInjection
public Storage<Integer, StudentResource> studentResources;
}
@@ -0,0 +1,93 @@
/*
* 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.conversion;
import com.zfoo.storage.strategy.JsonToArrayConverter;
import com.zfoo.storage.strategy.JsonToMapConverter;
import com.zfoo.storage.strategy.StringToClassConverter;
import com.zfoo.storage.strategy.StringToDateConverter;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author jaysunxiao
* @version 3.0
*/
public class ConversionTest {
private static final ConversionServiceFactoryBean csfb = new ConversionServiceFactoryBean();
private static final Set<Object> converters = new HashSet<>();
private static final StringToDateConverter std = new StringToDateConverter();
private static final StringToClassConverter stcc = new StringToClassConverter();
private static final JsonToMapConverter jtmc = new JsonToMapConverter();
private static final JsonToArrayConverter jtac = new JsonToArrayConverter();
static {
converters.add(std);
converters.add(stcc);
converters.add(jtmc);
converters.add(jtac);
csfb.setConverters(converters);
csfb.afterPropertiesSet();
}
@Test
public void string2Integer() {
ConversionService conversionService = csfb.getObject();
Integer result = conversionService.convert("123", Integer.class);
Assert.assertEquals(123, result.intValue());
}
@Test
public void string2Class() {
ConversionService conversionService = csfb.getObject();
// String to Class
Class<?> clazz = (Class<?>) conversionService.convert("com.zfoo.storage.model.vo.Storage", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Class.class));
Assert.assertEquals("com.zfoo.storage.model.vo.Storage", clazz.getName());
}
@Test
public void string2Map() {
ConversionService conversionService = csfb.getObject();
//Json to Map
String str = "{\"1\":1,\"2\":2,\"3\":3}";
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map = (Map<Integer, Integer>) conversionService.convert(str, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(map.getClass()));
Assert.assertEquals(3, map.size());
}
@Test
public void string2Array() {
ConversionService conversionService = csfb.getObject();
String str = "[1,2,3]";
Integer[] array = (Integer[]) conversionService.convert(str, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer[].class));
Assert.assertEquals(3, array.length);
}
}
@@ -0,0 +1,98 @@
/*
* 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.excel;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
/**
* @author jaysunxiao
* @version 3.0
*/
@Ignore
public class ExcelTest {
@Test
public void createExcelTest() throws IOException {
//第一步创建workbook
var wb = WorkbookFactory.create(true);
//第二步创建sheet
var sheet = wb.createSheet("测试");
//第三步创建行row:添加表头0行
var row = sheet.createRow(0);
var style = wb.createCellStyle();
//style.setAlignment(HSSFCellStyle.ALIGN_CENTER); //居中
//第四步创建单元格
var cell = row.createCell(0); //第一个单元格
cell.setCellValue("姓名");
cell.setCellStyle(style);
cell = row.createCell(1); //第二个单元格
cell.setCellValue("年龄");
cell.setCellStyle(style);
//第五步插入数据
for (int i = 0; i < 5; i++) {
//创建行
row = sheet.createRow(i + 1);
//创建单元格并且添加数据
row.createCell(0).setCellValue("aa" + i);
row.createCell(1).setCellValue(i);
}
//第六步将生成excel文件保存到指定路径下
FileOutputStream target = new FileOutputStream("target.xlsx");
wb.write(target);
target.close();
System.out.println("Excel文件生成成功...");
}
@Test
public void readExcelTest() throws IOException {
Workbook wb = WorkbookFactory.create(new File("target.xlsx"));
// 只读取第一个工作簿
Sheet sheet = wb.getSheetAt(0);
Iterator<Row> iterator = sheet.iterator();
while (iterator.hasNext()) {
Row row = iterator.next();
StringBuilder builder = new StringBuilder();
builder.append(row.getCell(0));
builder.append(" - ");
builder.append(row.getCell(1));
System.out.println(builder.toString());
}
System.out.println("Excel文件读取成功");
}
}
@@ -0,0 +1,40 @@
/*
* 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.pathmatch;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* @author jaysunxiao
* @version 3.0
*/
@Ignore
public class PathMatchTest {
@Test
public void resourceTest() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:com/zfoo/**/*.class");
for (Resource res : resources) {
System.out.println(res.getDescription());
}
}
}
@@ -0,0 +1,73 @@
/*
* 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 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 StudentResource {
@Id
private int id;
/**
* 索引,默认为可重复的索引
*/
@Index
private String name;
private int age;
private String[] courses;
private User[] users;
private User user;
/**
* 不想映射的字段必须加上transient关键字,这样就不会从Excel中去找对应的列
*/
private transient String notMapContent;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String[] getCourses() {
return courses;
}
public User[] getUsers() {
return users;
}
public User getUser() {
return user;
}
public String getNotMapContent() {
return notMapContent;
}
}
@@ -0,0 +1,42 @@
/*
* 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 User {
private String id;
private String name;
private String sex;
private int age;
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public int getAge() {
return age;
}
}
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
您也许已经注意到 XML 声明没有关闭标签。这不是错误。声明不属于XML本身的组成部分。
它不是 XML 元素,也不需要关闭标签。
-->
<!--
默认的命名空间(Default Namespaces)为元素定义默认的命名空间可以让我们省去在所有的子元素中使用前缀的工作。
使用语法:xmlns=".......namespaceURI",这样做的原因是每个xml都有一个根元素
-->
<!--
1.第一步:添加xmlns:xsi属性,引入xsi命名空间
2.第二步:添加命名空间,可添加多个,但没有前缀的命名空间最多只能有1个
3.第三步:通过xsi命名空间下的属性schemaLocation指定每个命名空间XSD文件的URI,命名空间和相应URI一一对应,多个命名空间用空格分隔
本xml文件中要用到某些来自xsi代表的“http://www.w3.org/2001/XMLSchema-instance”这个命名空间的元素
比如用来引入无命名空间schema文件的noNamespaceSchemaLocation="XXX";
以及引入自带命名空间的schema文件的schemaLocation="XXX"这些元素。
这些元素是包含在xsi命名空间中的,所有的xml文件只要引用这些元素 就要引入xsi这个命名空间。
xsi这三个字母不是硬性规定,只是大家都这么用,方便阅读而已。
xsi是http://www.w3.org/2001/XMLSchema-instance的别名。
第一个URI是名称空间的名字,第二个URI给出模式文档的位置
-->
<!--
一旦您拥有了可用的 XML Schema 实例命名空间:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
您就可以使用 schemaLocation 属性了。此属性有两个值。第一个值是需要使用的命名空间。
第二个值是供命名空间使用的 XML schema 的位置:
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:storage="http://www.zfoo.com/schema/storage"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.zfoo.com/schema/storage
http://www.zfoo.com/schema/storage-1.0.xsd">
<context:component-scan base-package="com.zfoo.storage"/>
<storage:storage id="resourceManager">
<storage:scan package="com.zfoo.**.resource"/>
<storage:resource location="classpath:/excel" suffix="xls"/>
</storage:storage>
</beans>
Binary file not shown.
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<contextName>com.zfoo.storage</contextName>
<property name="LOG_HOME" value="log/storage"/>
<property name="PATTERN_FILE"
value="%d{yyyy-MM-dd HH:mm:ss} [%5level] [%thread] %logger.%M\\(%F:%line\\) - %msg%n"/>
<property name="PATTERN_CONSOLE"
value="%d{yyyy-MM-dd HH:mm:ss} [%highlight(%5level)] [%thread] %logger.%M\\(%F:%line\\) - %msg%n"/>
<!-- 负责写日志,控制台日志,会打印所有的包的所有级别日志 -->
<appender name="zfoo_console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN_CONSOLE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 根logger -->
<root level="info">
<appender-ref ref="zfoo_console"/>
</root>
<!--
- 1.name:包名或类名,用来指定受此logger约束的某一个包或者具体的某一个类
- 2.未设置打印级别,所以继承他的上级<root>的日志级别“DEBUG”
- 3.未设置additivity,默认为true,将此logger的打印信息向上级传递;
- 4.未设置appender,此logger本身不打印任何信息,级别为“DEBUG”及大于“DEBUG”的日志信息传递给root,
- root接到下级传递的信息,交给已经配置好的名为“STDOUT”的appender处理,“STDOUT”appender将信息打印到控制台;
-->
<logger name="ch.qos.logback" level="info"/>
<!--*******************************************Spring********************************************************-->
<!--logger中的name是指代码的包名或类名,路径要写全,可以配置不同包中的日志输出到不同的文件中。level是日志输出级别 -->
<!--过滤掉spring的一些无用的DEBUG信息-->
<logger name="org.springframework" level="info"/>
<!-- additivity="false"表示不继承父logger的配置和父类没有关系-->
<logger name="org.springframework.core" level="warn"/>
<!--*******************************************Netty*********************************************************-->
<logger name="io.netty" level="info"/>
</configuration>