【Java】包扫描与Jar包扫描工具
为什么要用到包扫描?
包扫描一般用作于扫描出该包内所有带有某注解的类,并对之进行处理
包扫描可分为普通包扫描和Jar包扫描
包扫描工具代码:
1.普通包扫描
// 给定包名及当前文件currentfile
private void scanPackage(String packageName, File currentfile) {
// 获得currentfile内的所有文件
File[] files = currentfile.listFiles(new FileFilter() {
// 内部类方法:根据路径名判断当前是否是文件夹
// 是则返回true
// 否则要判断该文件是否是class类型文件,并返回结果
@Override
public boolean accept(File pathname) {
if (currentfile.isDirectory()) {
return true;
}
return pathname.getName().endsWith(".class");
}
});
// 遍历所有的file
// 若file是文件夹,则更新路径,递归扫描
for (File file : files) {
if (file.isDirectory()) {
scanPackage(packageName + "." + file.getName(), file);
} else {
// 若file非文件夹
// 则要根据目前包路径和file名得到类名
// 我们的包扫描是处理类的:
// 所以我不希望他是八大基本类型,注解,接口,及,枚举
String fileName = file.getName().replace(".class", "");
String className = packageName + "." + fileName;
try {
Class<?> klass = Class.forName(className);
if (klass.isAnnotation()
||klass.isInterface()
||klass.isEnum()
||klass.isPrimitive()) {
continue;
}
// 无论普通包扫描和Jar包扫描
// 我们都需要对扫描到的类进行处理
// 如何处理并不由我们扫描的工具决定
// 是由工具使用者决定的
// 所以下面的dealClass必然是抽象方法!
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
2.Jar包扫描
Jar包扫描的思路同普通包扫描
根据Jar包文件找出所有的类,当然要排除八大基本类型,接口,枚举和注解。
然后在通过用户完成对扫描出的class操作
private void scanPackage(URL url) throws IOException {
JarURLConnection jarUrlConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarUrlConnection.getJarFile();
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarName = jarEntry.getName();
if (jarEntry.isDirectory() || !jarName.endsWith(".class")) {
continue;
}
String className = jarName.replace(".class", "").replaceAll("/", ".");
try {
Class<?> klass = Class.forName(className);
if (klass.isAnnotation()
||klass.isInterface()
||klass.isEnum()
||klass.isPrimitive()) {
continue;
}
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3.当然,这样还不够“智能”。我能不能根据使用者给我的路径,“智能”判断其在扫描包还是Jar包,答案是肯定的。毕竟上面的都是private
// 重载:根据类进行类所在包的包扫描
public void packageScanner(Class<?> klass) {
packageScanner(klass.getPackage().getName());
}
// 根据包名进行包扫描
public void packageScanner(String packageName) {
// 将包名转换为路径
String packagePath = packageName.replace(".", "/");
// 得到当前类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
// 遍历路径下的所有,得到URL,根据URL判断其属性是否Jar
// 达到不同处理的目的
Enumeration<URL> resources = classLoader.getResources(packagePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
if (url.getProtocol().equals("jar")) {
scanPackage(url);
} else {
File file = new File(url.toURI());
if (!file.exists()) {
continue;
}
scanPackage(packageName, file);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
整体代码
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public abstract class PackageScanner {
public PackageScanner() {
}
public abstract void dealClass(Class<?> klass);
public void packageScanner(Class<?> klass) {
packageScanner(klass.getPackage().getName());
}
public void packageScanner(String packageName) {
String packagePath = packageName.replace(".", "/");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> resources = classLoader.getResources(packagePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
if (url.getProtocol().equals("jar")) {
scanPackage(url);
} else {
File file = new File(url.toURI());
if (!file.exists()) {
continue;
}
scanPackage(packageName, file);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private void scanPackage(URL url) throws IOException {
JarURLConnection jarUrlConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarUrlConnection.getJarFile();
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarName = jarEntry.getName();
if (jarEntry.isDirectory() || !jarName.endsWith(".class")) {
continue;
}
String className = jarName.replace(".class", "").replaceAll("/", ".");
try {
Class<?> klass = Class.forName(className);
if (klass.isAnnotation()
||klass.isInterface()
||klass.isEnum()
||klass.isPrimitive()) {
continue;
}
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private void scanPackage(String packageName, File currentfile) {
File[] files = currentfile.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (currentfile.isDirectory()) {
return true;
}
return pathname.getName().endsWith(".class");
}
});
for (File file : files) {
if (file.isDirectory()) {
scanPackage(packageName + "." + file.getName(), file);
} else {
String fileName = file.getName().replace(".class", "");
String className = packageName + "." + fileName;
try {
Class<?> klass = Class.forName(className);
if (klass.isAnnotation()
||klass.isInterface()
||klass.isEnum()
||klass.isPrimitive()) {
continue;
}
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
除构造方法外,提供两个public方法 void packageScanner(Class<?> klass)
和 void packageScanner(String packageName)
扫描出 给定类所在包 或 给定包路径 下的所有的类 (用到递归)
用抽象方法提供给工具使用者去决定 如何处理扫描出的类
使用方法:
public static void main(String[] args) {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
System.out.println(klass.getName());
}
}.packageScanner("util.core");
}
或
public static void main(String[] args) {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
System.out.println(klass.getName());
}
}.packageScanner(XXX.class);
}