Merge pull request #36 from Yuao-github/main

feat[storage]:配置文件可以同时指定多个扫描路径和扫描包
This commit is contained in:
godotg
2022-12-12 17:18:52 +08:00
committed by GitHub
7 changed files with 134 additions and 48 deletions
@@ -484,4 +484,39 @@ public abstract class StringUtils {
public static boolean isStopChar(char ch) {
return Character.isWhitespace(ch) || STOP_WORD.contains(ch);
}
/**
*
* @param str 待匹配字符串
* @param matchStr 匹配的后缀
* @return 是否成功匹配后缀
*/
public static boolean suffixMatch(final String str,final String matchStr){
if(matchStr.length()>str.length())
return false;
for(int i=0;i<matchStr.length();i++){
if(str.charAt(str.length()-1-i)!=matchStr.charAt(matchStr.length()-1-i))
return false;
}
return true;
}
/**
*把String数组展开成一个大的字符串
* @param strings
* @return
*/
public static String stringArrayToString(final String[] strings){
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append('[');
for(var i=0;i<strings.length;i++){
if(i!=0){
stringBuilder.append(',');
}
stringBuilder.append(StringUtils.format("[{}]",i+1));
stringBuilder.append(strings[i]);
}
stringBuilder.append(']');
return stringBuilder.toString();
}
}
@@ -24,6 +24,7 @@ import com.zfoo.storage.StorageContext;
import com.zfoo.storage.model.anno.Id;
import com.zfoo.storage.model.anno.ResInjection;
import com.zfoo.storage.model.config.StorageConfig;
import com.zfoo.storage.model.resource.ResourceEnum;
import com.zfoo.storage.model.vo.ResourceDef;
import com.zfoo.storage.model.vo.Storage;
import org.springframework.core.io.Resource;
@@ -75,7 +76,7 @@ public class StorageManager implements IStorageManager {
var resourceDefinitionMap = new HashMap<Class<?>, ResourceDef>();
// 扫描Excel的class类文件
var clazzNameSet = scanResourceAnno(storageConfig.getScanPackage());
var clazzNameSet = scanResourceAnno(storageConfig.getScanPackages());
// 通过class类文件扫描excel文件地址
for (var clazzName : clazzNameSet) {
@@ -221,22 +222,24 @@ public class StorageManager implements IStorageManager {
return getStorageConfig();
}
private Set<String> scanResourceAnno(String scanLocation) {
private Set<String> scanResourceAnno(String[] scanPackages) {
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());
for(var scanPackage:scanPackages) {
var packageSearchPath = ResourceUtils.CLASSPATH_URL_PREFIX + scanPackage.replace(StringUtils.PERIOD, StringUtils.SLASH) + StringUtils.SLASH + SUFFIX_PATTERN;
var resources = resourcePatternResolver.getResources(packageSearchPath);
var resourceName = 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(resourceName)) {
ClassMetadata clazzMeta = metadataReader.getClassMetadata();
result.add(clazzMeta.getClassName());
}
}
}
}
@@ -251,30 +254,48 @@ public class StorageManager implements IStorageManager {
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
try {
var resourceList = new ArrayList<Resource>();
var packageSearchPath = StringUtils.format("{}/**/{}.*", storageConfig.getResourceLocation(), clazz.getSimpleName());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
try {
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
} catch (Exception e) {
// do nothing
}
// 通配符无法匹配根目录,所以如果找不到,再从根目录查找一遍
if (CollectionUtils.isEmpty(resourceList)) {
packageSearchPath = StringUtils.format("{}/{}.*", storageConfig.getResourceLocation(), clazz.getSimpleName());
var resourceDefSet = new HashSet<ResourceDef>();
for(var resourceLocation: storageConfig.getResourceLocations()) {
var packageSearchPath = StringUtils.format("{}/**/{}.*", resourceLocation, clazz.getSimpleName());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
resourceList.addAll(Arrays.asList(resourcePatternResolver.getResources(packageSearchPath)));
try {
for(var resource :resourcePatternResolver.getResources(packageSearchPath)){
if(StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.CSV.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.EXCEL_XLS.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.EXCEL_XLSX.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.JSON.getType()))
resourceDefSet.add(new ResourceDef(clazz,resource));
}
} catch (Exception e) {
// do nothing
}
// 通配符无法匹配根目录,所以如果找不到,再从根目录查找一遍
if (resourceDefSet.isEmpty()) {
packageSearchPath = StringUtils.format("{}/{}.*", resourceLocation, clazz.getSimpleName());
packageSearchPath = packageSearchPath.replaceAll("//", "/");
for(var resource :resourcePatternResolver.getResources(packageSearchPath)){
if(StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.CSV.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.EXCEL_XLS.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.EXCEL_XLSX.getType())
||StringUtils.suffixMatch(resource.getFilename(), ResourceEnum.JSON.getType()))
resourceDefSet.add(new ResourceDef(clazz,resource));
}
}
}
if (CollectionUtils.isEmpty(resourceList)) {
throw new RuntimeException(StringUtils.format("无法找到配置文件[{}]", clazz.getSimpleName()));
} else if (resourceList.size() > 1) {
var resourceNames = resourceList.stream().map(it -> it.getFilename()).collect(Collectors.joining(StringUtils.COMMA));
throw new RuntimeException(StringUtils.format("资源类[class:{}]找到重复的配置文件[{}]", clazz.getSimpleName(), resourceNames));
if (CollectionUtils.isEmpty(resourceDefSet)) {
throw new RuntimeException(StringUtils.format("资源类[class:{}]无法找到配置文件", clazz.getSimpleName()));
} else if (resourceDefSet.size() > 1) {
var resourceNames=new String[resourceDefSet.size()];
var index=0;
for(var resourceDef:resourceDefSet){
resourceNames[index++]=resourceDef.getResource().getFile().getAbsolutePath();
}
throw new RuntimeException(StringUtils.format("资源类[class:{}]找到重复的配置文件{}", clazz.getSimpleName(),StringUtils.stringArrayToString(resourceNames)));
} else {
return resourceList.get(0);
return ((ResourceDef)(resourceDefSet.toArray()[0])).getResource();
}
} catch (IOException e) {
@@ -21,9 +21,9 @@ public class StorageConfig {
private String id;
private String scanPackage;
private String[] scanPackages;
private String resourceLocation;
private String[] resourceLocations;
// 类的属性是否可写,如果为false则类的属性必须为private并且不能有set方法
private boolean writeable;
@@ -39,20 +39,20 @@ public class StorageConfig {
this.id = id;
}
public String getScanPackage() {
return scanPackage;
public String[] getScanPackages() {
return scanPackages;
}
public void setScanPackage(String scanPackage) {
this.scanPackage = scanPackage;
public void setScanPackages(String[] scanPackages) {
this.scanPackages = scanPackages;
}
public String getResourceLocation() {
return resourceLocation;
public String[] getResourceLocations() {
return resourceLocations;
}
public void setResourceLocation(String resourceLocation) {
this.resourceLocation = resourceLocation;
public void setResourceLocations(String[] resourceLocations) {
this.resourceLocations = resourceLocations;
}
public boolean isWriteable() {
@@ -15,6 +15,9 @@ package com.zfoo.storage.model.vo;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Objects;
/**
* @author godotg
* @version 4.0
@@ -38,4 +41,28 @@ public class ResourceDef {
return resource;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ResourceDef that = (ResourceDef) o;
boolean equal= false;
try {
equal = this.resource.getFile().getAbsolutePath().equals(that.resource.getFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
return equal;
}
@Override
public int hashCode() {
int hashCode=0;
try {
hashCode=this.resource.getFile().getAbsolutePath().hashCode();
} catch (IOException e) {
e.printStackTrace();
}
return hashCode;
}
}
@@ -66,10 +66,10 @@ public class StorageDefinitionParser implements BeanDefinitionParser {
}
resolvePlaceholder("id", "id", builder, element, parserContext);
resolvePlaceholder("package", "scanPackage", builder, scanElement, parserContext);
resolvePlaceholder("package", "scanPackages", builder, scanElement, parserContext);
resolvePlaceholder("writeable", "writeable", builder, scanElement, parserContext);
resolvePlaceholder("recycle", "recycle", builder, scanElement, parserContext);
resolvePlaceholder("location", "resourceLocation", builder, resourceElement, parserContext);
resolvePlaceholder("location", "resourceLocations", builder, resourceElement, parserContext);
parserContext.getRegistry().registerBeanDefinition(clazz.getCanonicalName(), builder.getBeanDefinition());
}
@@ -90,6 +90,8 @@ public class StorageDefinitionParser implements BeanDefinitionParser {
private void resolvePlaceholder(String attributeName, String fieldName, BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
var attributeValue = element.getAttribute(attributeName);
attributeValue=attributeValue.replaceAll(";",",");
attributeValue=attributeValue.replaceAll(" ",",");
var environment = parserContext.getReaderContext().getEnvironment();
var placeholder = environment.resolvePlaceholders(attributeValue);
builder.addPropertyValue(fieldName, placeholder);
@@ -78,8 +78,8 @@ public class ExportBinaryTest {
@Test
public void test() throws Exception {
var config = new StorageConfig();
config.setScanPackage("com.zfoo.storage.export");
config.setResourceLocation("classpath:/excel");
config.setScanPackages(new String[]{"com.zfoo.storage.export"});
config.setResourceLocations(new String[]{"classpath:/excel"});
config.setWriteable(true);
config.setRecycle(false);
var storageManager = new StorageManager();
+3 -2
View File
@@ -47,9 +47,10 @@
<context:component-scan base-package="com.zfoo.storage"/>
<storage:storage id="resourceManager">
<!-- package以及location属性都支持同时指定多个路径,路径分隔符为, ;或者空格-->
<storage:scan package="com.zfoo.**.resource"/>
<!-- 如果是类路径一classpath开头,如果是其它目录则以file开头-->
<storage:resource location="classpath:/excel"/>
<!-- 如果是类路径一classpath开头,如果是其它目录则以file开头,-->
<storage:resource location="classpath:/,classpath:/excel"/>
<!-- <storage:resource location="file:C:\Users\godotg\Desktop\excel"/>-->
</storage:storage>