spring-core提供了资源扫描实现,可以获取到java程序运行时的所有类和其他资源,所以在spring环境下,也可以使用它来自己实现查找类的功能
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.12</version>
<scope>compile</scope>
</dependency>
实现代码:SpringResourceScanner.java
import java.io.IOException;
import java.net.URL;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* 基于Spring {@link PathMatchingResourcePatternResolver}实现资源扫描
* @author guyadong
*
*/
public class SpringResourceScanner {
private static final String CLASS_SUFFIX = ".class";
private static final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
private Set<URL> getResources(String rootDirPath, String suffix) throws IOException {
if(null==rootDirPath) {
throw new NullPointerException("rootDirPath must not be null");
}
String path = rootDirPath;
String locationPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + path;
if(null != suffix) {
locationPattern+="/**/*"+suffix;
}
Resource[] resources = resolver.getResources(locationPattern);
return Stream.of(resources).map(input->{
try {
return input.getURL();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toSet());
}
/**
* 扫描指定包名下的所有类
* @param packagename
* @param filter
* @throws IOException
*/
public Set<Class<?>> getClasses(String packagename) throws IOException {
if(null == packagename) {
throw new NullPointerException("packagename must not be null");
}
final String path = packagename.replace('.', '/');
Set<URL> rootDirs = getResources(path,null);
Set<URL> urls = getResources(path, CLASS_SUFFIX);
//final Set<String> rootPrefixes = rootDirs.stream().map(input->input.toString().substring(0,path.length)).collect(Collectors.toSet());
// 根据网友 @sp42a 建议修改,
// 字符替换操作改为只删除结尾内容,
// 避免包名与groupId相同时将jar所在路径也错误替换的问题,
// 如 jar:file:/path/com/ajaxjs/sqlman/1.0/sqlman-1.0.jar!/com/ajaxjs/sqlman
final Set<String> rootPrefixes = rootDirs.stream().map(URL::toString)
.map(input -> input.substring(0, input.length() - path.length())).collect(Collectors.toSet());
return urls.stream().map(new CLassBuilder(rootPrefixes)).collect(Collectors.toSet());
}
protected static class CLassBuilder implements Function<URL, Class<?>>{
private final Iterable<String> rootPrefixes;
public CLassBuilder(Iterable<String> rootPrefixes) {
this.rootPrefixes = rootPrefixes;
}
@Override
public Class<?> apply(URL input) {
String url = input.toString();
for(String prefix:rootPrefixes) {
if(url.startsWith(prefix)) {
try {
String p = url.substring(prefix.length(),url.length()-CLASS_SUFFIX.length());
String className = p.replace('/', '.');
return Class.forName( className,false,getDefaultClassLoader());
} catch (Throwable e) {
System.out.printf("%s:%s\n",e.getClass().getSimpleName(), e.getMessage());
return null;
}
}
};
throw new IllegalArgumentException("INVALID URL "+input);
}
}
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = SpringResourceScanner.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
}
调用示例:SpringResourceScannerTest.java
import static org.junit.Assert.*;
import java.util.Set;
import static net.gdface.utils.SimpleLog.log;
import org.junit.Test;
public class SpringResourceScannerTest {
public static final SpringResourceScanner SPRING_RESOURCE_SCANNER = new SpringResourceScanner();
@Test
public void test3SpringGetClasses() {
try {
Set<Class<?>> urls = SPRING_RESOURCE_SCANNER.getClasses("net.facelib.cell");
for(Class<?> url:urls) {
log("class:{}",url);
}
log("class count:{}",urls.size());
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void test4SpringGetClasses() {
try {
Set<Class<?>> urls = SPRING_RESOURCE_SCANNER.getClasses("org.springframework.core");
for(Class<?> url:urls) {
log("class:{}",url);
}
log("class count:{}",urls.size());
} catch (Throwable e) {
e.printStackTrace();
fail();
}
}
}