Skip to content

Commit 2bac314

Browse files
committed
包扫描工具更新
1 parent 7c1b8ff commit 2bac314

File tree

2 files changed

+152
-104
lines changed

2 files changed

+152
-104
lines changed

frame-util/src/main/java/com/github/emailtohl/frame/util/PackageScanner.java

Lines changed: 126 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import java.io.File;
44
import java.io.FileFilter;
55
import java.io.IOException;
6+
import java.io.UnsupportedEncodingException;
67
import java.net.JarURLConnection;
78
import java.net.URL;
89
import java.net.URLDecoder;
10+
import java.nio.charset.Charset;
911
import java.util.Enumeration;
1012
import java.util.LinkedHashSet;
1113
import java.util.Set;
@@ -15,112 +17,87 @@
1517
import java.util.logging.Logger;
1618

1719
/**
18-
* 扫描包,将其中的类提取到Set<Class<?>>容器中
20+
* 扫描包,获取包下的所有类
21+
*
22+
* @author HeLei
1923
*/
2024
public final class PackageScanner {
25+
private static final Logger logger = Logger.getLogger(PackageScanner.class.getName());
26+
2127
private PackageScanner() {
2228
}
2329

24-
private final static Logger logger = Logger.getLogger(PackageScanner.class.getName());
25-
26-
public static Set<Class<?>> getClasses(String pack) {
30+
/**
31+
* 查找包名下的所有类实例
32+
*
33+
* @param packageName 包名
34+
* @return 类实例集合
35+
*/
36+
public static Set<Class<?>> getClasses(String packageName) {
2737
// 第一个class类的集合
2838
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
29-
// 是否循环迭代
30-
boolean recursive = true;
3139
// 获取包的名字并进行替换
32-
String packageName = pack;
3340
String packageDirName = packageName.replace('.', '/');
34-
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
41+
// logger.debug(new ClassPathResource(packageDirName).getURI());
42+
// 定义一个枚举的集合 并进行循环来处理这个目录下的内容
3543
Enumeration<URL> dirs;
3644
try {
3745
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
38-
// 循环迭代下去
39-
while (dirs.hasMoreElements()) {
40-
// 获取下一个元素
41-
URL url = dirs.nextElement();
42-
// 得到协议的名称
43-
String protocol = url.getProtocol();
44-
// 如果是以文件的形式保存在服务器上
45-
if ("file".equals(protocol)) {
46-
logger.fine("file类型的扫描");
47-
// 获取包的物理路径
48-
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
49-
// 以文件的方式扫描整个包下的文件 并添加到集合中
50-
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
51-
} else if ("jar".equals(protocol)) {
52-
// 如果是jar包文件
53-
// 定义一个JarFile
54-
logger.fine("jar类型的扫描");
55-
JarFile jar;
56-
try {
57-
// 获取jar
58-
jar = ((JarURLConnection) url.openConnection()).getJarFile();
59-
// 从此jar包 得到一个枚举类
60-
Enumeration<JarEntry> entries = jar.entries();
61-
// 同样的进行循环迭代
62-
while (entries.hasMoreElements()) {
63-
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
64-
JarEntry entry = entries.nextElement();
65-
String name = entry.getName();
66-
// 如果是以/开头的
67-
if (name.charAt(0) == '/') {
68-
// 获取后面的字符串
69-
name = name.substring(1);
70-
}
71-
// 如果前半部分和定义的包名相同
72-
if (name.startsWith(packageDirName)) {
73-
int idx = name.lastIndexOf('/');
74-
// 如果以"/"结尾 是一个包
75-
if (idx != -1) {
76-
// 获取包名 把"/"替换成"."
77-
packageName = name.substring(0, idx).replace('/', '.');
78-
}
79-
// 如果可以迭代下去 并且是一个包
80-
if ((idx != -1) || recursive) {
81-
// 如果是一个.class文件 而且不是目录
82-
if (name.endsWith(".class") && !entry.isDirectory()) {
83-
// 去掉后面的".class" 获取真正的类名
84-
String className = name.substring(packageName.length() + 1, name.length() - 6);
85-
try {
86-
// 添加到classes
87-
classes.add(Class.forName(packageName + '.' + className));
88-
} catch (ClassNotFoundException e) {
89-
e.printStackTrace();
90-
logger.log(Level.SEVERE, "添加用户自定义视图类错误找不到此类的.class文件", e);
91-
}
92-
}
93-
}
94-
}
95-
}
96-
} catch (IOException e) {
97-
e.printStackTrace();
98-
logger.log(Level.SEVERE, "在扫描用户定义视图时从jar包获取文件出错", e);
99-
}
46+
} catch (IOException e) {
47+
logger.log(Level.WARNING, packageName + " read failed!", e);
48+
throw new IllegalArgumentException(e);
49+
}
50+
// 循环迭代下去
51+
while (dirs.hasMoreElements()) {
52+
// 获取下一个元素
53+
URL url = dirs.nextElement();
54+
// 得到协议的名称
55+
String protocol = url.getProtocol();
56+
// 如果是以文件的形式保存在服务器上
57+
if ("file".equals(protocol)) {
58+
logger.fine("scan file system");
59+
// 获取包的物理路径
60+
String filePath;
61+
try {
62+
filePath = URLDecoder.decode(url.getFile(), Charset.defaultCharset().name());
63+
} catch (UnsupportedEncodingException e) {
64+
logger.log(Level.WARNING, e.getMessage(), e);
65+
continue;
10066
}
67+
// 以文件的方式扫描整个包下的文件 并添加到集合中
68+
findClassesByFileSystem(packageName, filePath, true, classes);
69+
} else if ("jar".equals(protocol)) {
70+
// 如果是jar包文件
71+
// 定义一个JarFile
72+
logger.fine("scan jar package");
73+
JarFile jar = null;
74+
// 获取jar
75+
try {
76+
jar = ((JarURLConnection) url.openConnection()).getJarFile();
77+
} catch (IOException e) {
78+
logger.log(Level.WARNING, "jar scan error", e);
79+
continue;
80+
}
81+
findClassesByJar(jar, packageName, true, classes);
10182
}
102-
} catch (IOException e) {
103-
e.printStackTrace();
104-
logger.log(Level.SEVERE, "获取类信息失败", e);
10583
}
10684
return classes;
10785
}
10886

10987
/**
110-
* 以文件的形式来获取包下的所有Class
88+
* 以文件的形式来获取包下的所有类实例
11189
*
112-
* @param packageName
113-
* @param packagePath
114-
* @param recursive
115-
* @param classes
90+
* @param packageName 包名,用于classLoader加载
91+
* @param filePath 查找类的目录
92+
* @param recursive 递归查找
93+
* @param classes 存放类实例的集合
11694
*/
117-
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
95+
public static void findClassesByFileSystem(String packageName, String filePath, final boolean recursive,
11896
Set<Class<?>> classes) {
119-
// 获取此包的目录 建立一个File
120-
File dir = new File(packagePath);
97+
File dir = new File(filePath);
12198
// 如果不存在或者 也不是目录就直接返回
12299
if (!dir.exists() || !dir.isDirectory()) {
123-
logger.fine("用户定义包名 " + packageName + " 下没有任何文件");
100+
logger.fine(String.format("用户定义包 {} 下没有任何文件", packageName));
124101
return;
125102
}
126103
// 如果存在 就获取包下的所有文件 包括目录
@@ -130,31 +107,76 @@ public boolean accept(File file) {
130107
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
131108
}
132109
});
133-
// 循环所有文件
134-
for (File file : dirfiles) {
135-
// 如果是目录 则继续扫描
136-
if (file.isDirectory()) {
137-
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
138-
classes);
139-
} else {
140-
// 如果是java类文件 去掉后面的.class 只留下类名
141-
String className = file.getName().substring(0, file.getName().length() - 6);
142-
try {
143-
// 添加到集合中去
144-
// classes.add(Class.forName(packageName + '.' + className));
145-
// 这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
146-
classes.add(
147-
Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
148-
} catch (ClassNotFoundException e) {
149-
e.printStackTrace();
150-
logger.log(Level.SEVERE, "添加用户自定义视图类错误 找不到此类的.class文件", e);
110+
if (dirfiles instanceof File[]) {
111+
// 循环所有文件
112+
for (File file : dirfiles) {
113+
// 如果是目录 则继续扫描
114+
if (file.isDirectory()) {
115+
findClassesByFileSystem(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
116+
classes);
117+
} else {
118+
// 如果是java类文件 去掉后面的.class 只留下类名
119+
String className = file.getName().substring(0, file.getName().length() - 6);
120+
try {
121+
// 添加到集合中去
122+
// classes.add(Class.forName(packageName + '.' + className));
123+
// 这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
124+
classes.add(Thread.currentThread().getContextClassLoader()
125+
.loadClass(packageName + '.' + className));
126+
} catch (ClassNotFoundException e) {
127+
logger.log(Level.INFO, "添加用户自定义视图类错误 找不到此类的.class文件", e);
128+
}
151129
}
152130
}
153131
}
154132
}
155133

156-
/* public static void main(String[] args) {
157-
Set<Class<?>> set = getClasses("mine.frame");
158-
System.out.println(set);
159-
}*/
134+
/**
135+
* 从jar包中获取所有类的实例
136+
*
137+
* @param jar jar文件
138+
* @param packageName 扫描的包路径
139+
* @param recursive 递归查找
140+
* @param classes 存放类实例的集合
141+
*/
142+
public static void findClassesByJar(JarFile jar, String packageName, boolean recursive, Set<Class<?>> classes) {
143+
// 获取包的名字并进行替换
144+
String packageDirName = packageName.replace('.', '/');
145+
// 从此jar包 得到一个枚举类
146+
Enumeration<JarEntry> entries = jar.entries();
147+
// 同样的进行循环迭代
148+
while (entries.hasMoreElements()) {
149+
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
150+
JarEntry entry = entries.nextElement();
151+
String name = entry.getName();
152+
// 如果是以/开头的
153+
if (name.charAt(0) == '/') {
154+
// 获取后面的字符串
155+
name = name.substring(1);
156+
}
157+
// 如果前半部分和定义的包名相同
158+
if (name.startsWith(packageDirName)) {
159+
int idx = name.lastIndexOf('/');
160+
// 如果以"/"结尾 是一个包
161+
if (idx != -1) {
162+
// 获取包名 把"/"替换成"."
163+
packageName = name.substring(0, idx).replace('/', '.');
164+
}
165+
// 如果可以迭代下去 并且是一个包
166+
if ((idx != -1) || recursive) {
167+
// 如果是一个.class文件 而且不是目录
168+
if (name.endsWith(".class") && !entry.isDirectory()) {
169+
// 去掉后面的".class" 获取真正的类名
170+
String className = name.substring(packageName.length() + 1, name.length() - 6);
171+
try {
172+
// 添加到classes
173+
classes.add(Class.forName(packageName + '.' + className));
174+
} catch (ClassNotFoundException e) {
175+
logger.log(Level.INFO, packageName + '.' + className + " not found", e);
176+
}
177+
}
178+
}
179+
}
180+
}
181+
}
160182
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.github.emailtohl.frame.util;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import java.util.Set;
7+
8+
import org.junit.Test;
9+
10+
public class PackageScannerTest {
11+
12+
@Test
13+
public void testGetClasses() {
14+
Set<Class<?>> set = PackageScanner.getClasses("com.github.emailtohl.frame");
15+
assertFalse(set.isEmpty());
16+
assertTrue(set.contains(PackageScanner.class));
17+
}
18+
19+
@Test
20+
public void testScanJar() {
21+
Set<Class<?>> set = PackageScanner.getClasses("org.junit");
22+
assertFalse(set.isEmpty());
23+
assertTrue(set.contains(Test.class));
24+
}
25+
26+
}

0 commit comments

Comments
 (0)