/**
* 扫描一个类所在目录下的所有类的类名
*
* @param clazz 要扫描的基础类
* @param filter 过滤器 为空的时候不过滤
* @return 一个全路径类名的List
*/
public static List<String> scanAllClass(Class clazz, int back, Function<String, Boolean> filter) {
List<String> classes = new LinkedList<>();
try {
URL url = clazz.getResource("");
StringBuilder sb = new StringBuilder(URLDecoder.decode(url.getPath(), "UTF-8"));
//取父目录 0 或者null的时候是本级不用变 但是1的时候要+1 是因为要把最后一个/也去掉
int level = back == 0 ? 0 : 1 + back;
for (int i = 0; i < level; i++) {
int index = sb.lastIndexOf("/");
sb = sb.delete(index, sb.length());
}
//补回最后一个/
if (level > 0) {
sb.append("/");
}
String decode = sb.toString();
System.out.println(decode);
//判断是jar包还是文件夹
String[] split = decode.split("\\.jar!/");
if (split.length > 1) {
//打开jar文件
JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile jarFile = connection.getJarFile();
Enumeration<JarEntry> jarEntries = jarFile.entries();
//遍历jar包下的所有文件
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
//查找出类
if (jarEntryName.startsWith(split[split.length - 1]) && jarEntryName.endsWith(".class")) {
String className = jarEntryName.replace("/", ".");
//执行过滤器
if (filter != null && !filter.apply(className)) {
continue;
}
classes.add(className.replaceAll(".class$", ""));
}
}
} else {
//todo mac 下测试效果
//计算出从包名开始往下的路径
//linux 下和windows下有所区别 windows下的decode 前面会加上一个 / 所以分隔符是\的时候 subNum要+1
String separator = System.getProperty("file.separator");
int subNum = "\\".equals(separator) ? 2 : 1;
int bathLength = decode.length() - subNum - clazz.getPackage().getName().length();
//BFS
Queue<File> queue = new LinkedList<>();
File file = new File(decode);
queue.add(file);
while (!queue.isEmpty()) {
for (int i = 0; i < queue.size(); i++) {
File poll = queue.poll();
String absolutePath = poll.getCanonicalPath();
if (absolutePath.endsWith(".class")) {
String substring = absolutePath.substring(bathLength);
String className = substring.replace(separator, ".");
//执行过滤器
if (filter != null && !filter.apply(className)) {
continue;
}
//将分割符切换成.
classes.add(className.replaceAll(".class$", ""));
} else {
if (poll.canRead() && poll.isDirectory()) {
File[] files = poll.listFiles();
//如果有子文件 入队
for (File f : files) {
queue.add(f);
}
}
}
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return classes;
}
public static void main(String[] args) {
System.out.println(scanAllClass(DataMemoryApplication.class, null));
System.out.println(scanAllClass(org.springframework.cache.Cache.class, null));
//过滤内部类
System.out.println(scanAllClass(org.springframework.cache.Cache.class, s -> !s.contains("$")));
}