1 包扫描工具
1.1 包扫描工具的作用
先前实现了通过File类来实现对一个目录下所有文件的遍历
但单纯的遍历出所有文件并没有什么实际意义,但如果可以通过扫描一个工程下所有文件,通过某种条件筛选出想要的文件,那么能做的事就很多了
比如得到带有特定注解的类,通过注解得到需要的信息
1.2 包扫描工具的实现
通过一个方法其参数只有字符串类型的目录路径,就可以得到本工程该路径下的所有class文件并做出处理,这里处理的步骤交给用户来完成,所以处理class文件的具体过程做成抽象方法,工具只实现找到该目录下的所有class文件
/**
* @author 雫
* @date 2021/2/4 - 12:05
* @function 扫描工程下的所有包和jar包
* 抽象方法用于用户对扫描结果的处理
*/
public abstract class AbstractPackageScanner {
public AbstractPackageScanner() {
}
/**
* @Author 雫
* @Description 交给用户处理类的抽象方法
* @Date 2021/2/4 10:34
* @Param []
* @return void
**/
public abstract void dealClass(Class<?> klass);
/**
* @Author 雫
* @Description 根据给定的包名
* 扫描指定包下的所有类,接口,枚举,注解和jar文件
* @Date 2021/2/4 10:29
* @Param [packageName]
* @return void
**/
public void packageScan(String packageName) throws Exception{
//将包名间的 . 转换成路径的 /
String pathName = packageName.replace('.', '/');
//获取当前的线程的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
/*通过类加载器获取给定路径下文件的URL
* (统一资源定位符也可用于目录路径表达方式,组成:协议+文件路径)
* 将该URL存入枚举中,URL表达路径是 / 和win系统表达路径的 \ 不一样*/
Enumeration<URL> urls = classLoader.getResources(pathName);
/*得到包名对应的URL,URL组成由协议+文件路径组成,获取协议可以使用 URL.getProtocol,协议有file等*/
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
/*处理文件URL,将该URL转换成File类型对象,此时得到的file的绝对路径有了 \ 即win系统下的文件目录*/
if("file".equals(url.getProtocol())) {
File file = new File(url.toURI());
/*有了file(file可以是目录也可以是文件),接下来获取file下的所有文件
* 一个目录下可能有若干个目录和文件,目录下可能又有子目录
* 那么就需要递归来获取目标目录下的所有文件
* 递归的起点就是file,每递归一次回到file继续递归直到file下所有目录都被遍历过*/
/*开始递归,获取目录下的所有.class文件并获取Class对象交给dealClass处理*/
scanFile(file, packageName);
/*处理jar文件,*/
} else if("jar".equals(url.getProtocol())) {
scanJar(url);
}
}
}
/**
* @Author 雫
* @Description 根据给定的File类型的目录来完成该目录下的递归
* 从而得到目录下的所有.class文件,进而构造出完整类名来构造出Class对象交给dealClass处理
* 这里会扫描出Annotation,Interface,Enum和Class,交给用户选择处理什么,怎么处理
* @Date 2021/2/4 11:24
* @Param [dir, packageName]
* @return void
**/
public void scanFile(File dir, String packageName) throws Exception {
File[] files = dir.listFiles();
for(File file : files) {
/*遍历file下的所有目录和文件,对目录和文件分别处理*/
if(file.isDirectory()) {
/*如果是目录,将该目录当作file调用scanFile递归该目录下的所有目录和文件直到目录被遍历完
* 且将包名扩展,以便得到完整的包名用于生成Class对象*/
scanFile(file, packageName + "." + file.getName());
/*如果是class类型的文件,构造出类名通过Class.forName获取Class对象
交给用户处理,即交给抽象方法dealClass*/
} else if(file.isFile() && file.getName().endsWith(".class")) {
/*构造出形如com.coisini.packageScanner.support1.ENetCommand的类名*/
String className = packageName + "." + file.getName().replace(".class", "");
Class<?> klass = Class.forName(className);
dealClass(klass);
}
}
}
/**
* @Author 雫
* @Description 对jar包内文件的处理
* 得到jar包内所有目录和文件的JarEntry枚举并遍历
* 筛选掉所有非class文件和目录
* @Date 2021/2/4 11:44
* @Param [url]
* @return void
**/
public void scanJar(URL url) throws Exception {
//根据获得的URL将其转换为JarURL连接
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
/*获得jarURL下的所有包含class文件的目录和class文件*/
JarFile jarFile = jarURLConnection.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
/*遍历存放带class文件的目录或class文件,排除非class文件的所有元素,即扫描出了jar包内的所有class文件*/
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(entry.isDirectory() || !entry.getName().endsWith(".class")) {
continue;
}
/*现在得到了带 / 的class文件路径,接下来将其转换成 . 且去掉.class
* 得到了jar包内的完整类名称,有了完整类名调用dealClass交给用户处理*/
String className = entry.getName().replace(".class", "");
className = className.replace("/", ".");
Class<?> klass = Class.forName(className);
dealClass(klass);
}
}
}
测试结果:
这样就可以取得某个工程下某目录内的所有class文件,并得到Class对象通过反射机制来进行处理,如解析注解等