使用Java寫出掃描包的工具

使用Java寫出掃描包的工具

1.前言

  通過掃描包的工具,可以使Java在運行時動態加載類。

2.正文

本文主要是帶你們如何製作一個簡單的掃描包的工具。主要思想分爲,第一步通過獲取當前線程來獲取到當前文件的根目錄,第二步根據根目錄進行遞歸掃描,若結尾位.class則爲Java類,第三步把該類的全限定名放入容器中,方便後續利用反射加載該類。

2.1.第一步思想

Class.getResource("");是獲取該文件的絕對路徑,Class.getResource("/");是獲取該項目的絕對路徑。

private File replacePointToFileString() {
    File file;
    if (EMPTY_STRING.equals(basePackage)) {
        file = new File(
                Thread.currentThread().getContextClassLoader().getResource(EMPTY_STRING).getPath(),
                SPRIT_STRING
        );
    } else {
        file = new File(
                Thread.currentThread().getContextClassLoader().getResource(EMPTY_STRING).getPath(),
                basePackage.replace(POINT_CHAR, SPRIT_CHAR)
        );
    }

    return file;
}

2.2.第二步思想

主要是通過簡單的遞歸來獲取class文件。在遞歸之前,先做一些準備工作,如下。

private List<String> findClasses(File file) {
    List<String> classPathContainer = new ArrayList<>();
    String basePackageLocal;
    int index = basePackage.lastIndexOf(POINT_STRING);

    if (!EMPTY_STRING.equals(basePackage) && index != -1) {
        basePackageLocal = basePackage.substring(0, index);
    } else {
        basePackageLocal = EMPTY_STRING;
    }

    findAnyPathJava(file, classPathContainer, basePackageLocal);

    return classPathContainer;
}

開始遞歸,如下所示,需要注意的是最後一個參數就是全限定名的前綴,利用了遞歸的特性,仔細琢磨琢磨。


private void findAnyPathJava(File file, List<String> container, String name) {
    if (file.isDirectory() && file.listFiles() != null) {
        for (File childFile : file.listFiles()) {
            if (EMPTY_STRING.equals(name)) {
                findAnyPathJava(childFile, container, file.getName());
                continue;
            }

            findAnyPathJava(childFile, container, name + POINT_STRING + file.getName());
        }
    } else if (file.isFile() && file.getName().endsWith(POINT_CLASS_STRING)) {
        container.add(name + POINT_STRING +
                file.getName().substring(0, file.getName().lastIndexOf(POINT_STRING)));
    }
}

2.3.全部代碼

package vip.wulang.spring.scanner;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * The class is used for scanning packages.
 *
 * @author CoolerWu on 2018/12/9.
 * @version 1.0
 */
@SuppressWarnings("all")
public class PackageScanner {
    private static final String EMPTY_STRING = "";
    private static final String POINT_STRING = ".";
    private static final char POINT_CHAR = '.';
    private static final char SPRIT_CHAR = '/';
    private static final String POINT_CLASS_STRING = ".class";
    private static final String SPRIT_STRING = "/";

    private String basePackage;

    public PackageScanner() {
        basePackage = EMPTY_STRING;
    }

    public PackageScanner(String basePackage) {
        this.basePackage = basePackage;
    }

    public List<String> startPackageScan() {
        File rootFile = replacePointToFileString();
        List<String> container = findClasses(rootFile);

        return container;
    }

    private File replacePointToFileString() {
        File file;
        if (EMPTY_STRING.equals(basePackage)) {
            file = new File(
                    Thread.currentThread().getContextClassLoader().getResource(EMPTY_STRING).getPath(),
                    SPRIT_STRING
            );
        } else {
            file = new File(
                    Thread.currentThread().getContextClassLoader().getResource(EMPTY_STRING).getPath(),
                    basePackage.replace(POINT_CHAR, SPRIT_CHAR)
            );
        }

        return file;
    }

    private List<String> findClasses(File file) {
        List<String> classPathContainer = new ArrayList<>();
        String basePackageLocal;
        int index = basePackage.lastIndexOf(POINT_STRING);

        if (!EMPTY_STRING.equals(basePackage) && index != -1) {
            basePackageLocal = basePackage.substring(0, index);
        } else {
            basePackageLocal = EMPTY_STRING;
        }

        findAnyPathJava(file, classPathContainer, basePackageLocal);

        return classPathContainer;
    }

    private void findAnyPathJava(File file, List<String> container, String name) {
        if (file.isDirectory() && file.listFiles() != null) {
            for (File childFile : file.listFiles()) {
                if (EMPTY_STRING.equals(name)) {
                    findAnyPathJava(childFile, container, file.getName());
                    continue;
                }

                findAnyPathJava(childFile, container, name + POINT_STRING + file.getName());
            }
        } else if (file.isFile() && file.getName().endsWith(POINT_CLASS_STRING)) {
            container.add(name + POINT_STRING +
                    file.getName().substring(0, file.getName().lastIndexOf(POINT_STRING)));
        }
    }
}

其中類中 startPackageScan() 方法是啓動整個流程代碼執行的啓動器。replacePointToFileString() 方法是把類似於"vip.wulang.base",轉換成"F://Dev/Demo/target/classes/vip/wulang/base",這樣我們就可以使用 File 類進行遞歸操作。findAnyPathJava() 就是一直遞歸,把Java文件的全限定名放進 container 容器中,具體算法稍微讀一下就可以知道原理了。

3.運行測試及結果

package vip.wulang.test;

import vip.wulang.spring.scanner.PackageScanner;

/**
 * @author CoolerWu on 2018/12/12.
 * @version 1.0
 */
public class PackageScannerTest {
    public static void main(String[] args) {
        System.out.println(new PackageScanner().startPackageScan());
    }
}

顯示結果
現在我們有了所有Java類的全限定名,可以用:Class.forName(“vip.wulang.spring.core.User”),會獲取到該類的class文件,也就是加載該文件到內存中。

4.結語

學如逆水行舟,不進則退。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章