版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
包扫描的本质
包扫描的实质扫描的是所给包中的class文件,且jar包用普通包的扫描方法来扫描是不正确的。
实现包扫描的核心思路
给出一个需要扫描的包的名称,首先进行判断,该包是jar包还是普通包
public void packageScann(String PackageName) {
String rootname = "";
//将包名转换为目录形式
rootname = PackageName.replace('.', '/');
//通过给出的包名转换成的目录名称得到当前目录的URL
URL url = Thread.currentThread().getContextClassLoader().getResource(rootname);
//通过url.getProtocol().equals()方法来判断是File文件目录还是Jar包
if (url.getProtocol().equals("file")) {
try {
URI uri = url.toURI();
File file = new File(uri);
//已检测是目录路径,通过dealDirectory()方法来处理
dealDirectory(PackageName, file);
} catch (URISyntaxException e) {
e.printStackTrace();
}
} else if (url.getProtocol().equals("jar")) {
//否则是jar包,通过dealJarPackage()方法来处理,此方法只需URL作为参数
dealJarPackage(url);
}
}
处理普通包
处理目录路径dealDirectory()此时分成两种情况:一是已经是java的class文件,二是仍然未到最底层还是目录
public void dealDirectory(String PackageName, File file) {
//通过当前目录即参数中的File类型的file,可以得到当前目录下的所有文件
File[] fileList = file.listFiles();
//便利这个list,以此处理每一个file
for (File files : fileList) {
//判断是否仍是目录路径,若是,调用dealDirectory()方法
if (files.isDirectory()) {
//但对于参数中的PackageName路径要增加一层,即在末尾追加“.files.getName()”,一定要注意不能省略中间的“.”
dealDirectory(PackageName + "." + files.getName(), files);
}
//此时判断是否已经是class文件,即不再是目录,已经不再有分支
else if (files.isFile()){
//处理class文件
dealClassFile(PackageName, files);
}
}
}
当前已经是ClassFile,进行对该class的完善处理
public void dealClassFile(String packageName, File file) {
//此时通过file.getName()得到的是文件名,形式为:XXX.XXXX
String className = file.getName();
//判断一下是否为class文件,即是否以".class"结尾
if (className.endsWith(".class")) {
//将末尾的.class去掉
className = className.replace(".class", "");
try {
//这里通过反射机制,处理经过完善的该class文件的名字(此时已经是该class文件多对应的类名)
Class<?> klass = Class.forName(packageName + "." + className);
//调用dealClass()方法,该方法是一个抽象方法,需要在用户使用该工具时对其进行实现,可以在不同的场景下有不同的处理方式
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
其中有一个抽象方法dealClass,是在已经到了目录最底层的class文件之后,通过对class文件获取名字,进行完善,即前面加上包名,形成一个完整的类名,通过反射机制反射出该类的类型,对于后序得到的Class<?>的通过抽象方法来扩展工具的适用范围,用户根据需求来做不同的处理
处理Jar包
jar包需要特殊处理,根据代码进行解析
private void dealJarPackage(URL url) {
try {
//JarURLConnection类通过JAR协议建立了一个访问 jar包URL的连接,可以访问这个jar包或者这个包里的某个文件
JarURLConnection connection = (JarURLConnection) url.openConnection();
//通过这个连接进一步先得到JarFile
JarFile jarFile = connection.getJarFile();
//得到该JarFile目录下所有项目
Enumeration<JarEntry> jarEntries = jarFile.entries();
//遍历得到的jarEntries
while (jarEntries.hasMoreElements()) {
JarEntry jar = jarEntries.nextElement();
//如果是目录路径或者不是class文件,不予处理
if(jar.isDirectory() || !jar.getName().endsWith(".class")) {
continue;
}
//以上是对于Jar包的特殊处理,由于Jar包与普通包不一样,其中一些文件也不能直接以普通文件的方式来处理
//以下与前面处理的方式一样
String jarName = jar.getName();
jarName = jarName.replace(".class", "");
jarName = jarName.replace("/", ".");
try {
Class<?> klass = Class.forName(jarName);
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
以上是对于包扫描的分段解析,下面附上全部代码:
public abstract class PackageScanner {
public PackageScanner() {
}
public abstract void dealClass(Class<?> klass);
public void dealClassFile(String packageName, File file) {
//此时通过file.getName()得到的是文件名,形式为:XXX.XXXX
String className = file.getName();
//判断一下是否为class文件,即是否以".class"结尾
if (className.endsWith(".class")) {
//将末尾的.class去掉
className = className.replace(".class", "");
try {
//这里通过反射机制,处理经过完善的该class文件的名字(此时已经是该class文件多对应的类名)
Class<?> klass = Class.forName(packageName + "." + className);
//调用dealClass()方法,该方法是一个抽象方法,需要在用户使用该工具时对其进行实现,可以在不同的场景下有不同的处理方式
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private void dealJarPackage(URL url) {
try {
//JarURLConnection类通过JAR协议建立了一个访问 jar包URL的连接,可以访问这个jar包或者这个包里的某个文件
JarURLConnection connection = (JarURLConnection) url.openConnection();
//通过这个连接进一步先得到JarFile
JarFile jarFile = connection.getJarFile();
//得到该JarFile目录下所有项目
Enumeration<JarEntry> jarEntries = jarFile.entries();
//遍历得到的jarEntries
while (jarEntries.hasMoreElements()) {
JarEntry jar = jarEntries.nextElement();
//如果是目录路径或者不是class文件,不予处理
if(jar.isDirectory() || !jar.getName().endsWith(".class")) {
continue;
}
//以上是对于Jar包的特殊处理,由于Jar包与普通包不一样,其中一些文件也不能直接以普通文件的方式来处理
//以下与前面处理的方式一样
String jarName = jar.getName();
jarName = jarName.replace(".class", "");
jarName = jarName.replace("/", ".");
try {
Class<?> klass = Class.forName(jarName);
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void dealDirectory(String PackageName, File file) {
//通过当前目录即参数中的File类型的file,可以得到当前目录下的所有文件
File[] fileList = file.listFiles();
//便利这个list,以此处理每一个file
for (File files : fileList) {
//判断是否仍是目录路径,若是,调用dealDirectory()方法
if (files.isDirectory()) {
//但对于参数中的PackageName路径要增加一层,即在末尾追加“.files.getName()”,一定要注意不能省略中间的“.”
dealDirectory(PackageName + "." + files.getName(), files);
}
//此时判断是否已经是class文件,即不再是目录,已经不再有分支
else if (files.isFile()){
//处理class文件
dealClassFile(PackageName, files);
}
}
}
public void packageScann(String PackageName) {
String rootname = "";
//将包名转换为目录形式
rootname = PackageName.replace('.', '/');
//通过给出的包名转换成的目录名称得到当前目录的URL
URL url = Thread.currentThread().getContextClassLoader().getResource(rootname);
//通过url.getProtocol().equals()方法来判断是File文件目录还是Jar包
if (url.getProtocol().equals("file")) {
try {
URI uri = url.toURI();
File file = new File(uri);
//已检测是目录路径,通过dealDirectory()方法来处理
dealDirectory(PackageName, file);
} catch (URISyntaxException e) {
e.printStackTrace();
}
} else if (url.getProtocol().equals("jar")) {
//否则是jar包,通过dealJarPackage()方法来处理,此方法只需URL作为参数
dealJarPackage(url);
}
}
}
测试如下
结果为