Java SE 实战篇 C9 包扫描工具

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对象通过反射机制来进行处理,如解析注解等

猜你喜欢

转载自blog.csdn.net/weixin_43541094/article/details/113637757
C9
今日推荐