Java *.class 编译文件构成了 Java 程序,而 Java 程序则通过 classpath 能够访问到 class 文件。一个 Java 程序,classpath 位置可以不止一个,而保存 class 的介质也不止一种,磁盘文件系统,jar 包甚至 zip 包这些介质都可以让 Java 程序读取,classpath 只是 JVM 对于其程序抽象的一个文件访问系统罢了。每个位置都可以用 URL 来描述,不同的位置之间用分号分隔。
特别地,JVM 程序能访问的不仅仅是 *.class,还可以是非 class 文件,于是我们可以称其“资源文件”。同样,他们可以在目录,或者打包到 jar 包里面中,只要定义为 classpath 可以访问的位置即可。
本文提到的完整源码在 : https://gitee.com/sp42_admin/ajaxjs-base/tree/master/src/main/com/ajaxjs/io/resource, 它是 AJAXJS 框架 的一部分。
扫描器的原理不复杂,首先是对输入的 Java 包名例如 com.foo.bar 转为 URL 对象。
然后得到 URL 集合的 Enumeration 枚举类型,——因为包名下面可能多个目录的。最终如上述所见,分别对 file、jar、zip 得到的资源进行处理,交由子类的覆写方法决定是否需要这些的资源。具体怎么判断是否需要该资源我们下面再讲。
AbstractScanner 是一个抽象类,泛型 T 是欲查找的目标类型。符合条件的资源保存在一个 set 之中。LinkedHashSet 是 Set 集合的一个实现,具有 set 集合不重复的特点,具有可插入的顺序的特定,比较适合当前场景。
扫描器可以扫描任何保存在 classpath 的东西,当然一般情况下我们都是有特定范围的查找。于是 fileFilter 就被派上用场了。
抽象类 AbstractScanner 抽象以下三个方法,
怎么给出实现呢?这里以 ScanClass 为例子,说明查找只是 *.class 编译文件。
这样就收集了 class 的路径。注意只是 String 类型的资源路径。而 AbstractScanner 这里期待的类型是 Class,于是就有一个 path 转换为 Class 类型的过程,由实现方法 onFileAdding/onJarAdding 完成。
前面例子是 Class,且有特定的 Java 给泛型 T 所使用——如果查找的资源是 *.txt 文件,能否呢?答案是肯定,这时我们把 T 声明为 Object,然后 FileFilter 改为判断 txt 后缀即可。下面是完整的实现。
前面我们提到有 ScannClass,用得比较多的是其静态方法:
可见 Object 作为泛型参数就可以表示不同类型的 class。现在我们缩窄下范围,只要求 IController 类型的 Class。
通过 class 的 isAssignableFrom() 方法可得知该类是否实现了 class 接口。