mini-ssm Lab1 - 包扫描

132 阅读2分钟

mini-ssm Lab1 - 包扫描

mini-ssm github

在第一个 Lab 中,我们将创建项目并实现包扫描功能。包扫描是 Spring 框架中用于自动发现和注册类的重要特性,我们通过实现一个简单的版本,了解这一核心机制的底层原理。

项目结构

我们先看一下项目的整体结构:

  • 项目结构

    image.png

  • Lab1 结构

    image.png

代码说明

在 Lab1 中,主要包含以下几个类和功能模块:

  • ClassUtil:实现包扫描逻辑并加载指定包下的所有 Class 对象。
  • Assert:从 Spring 框架中复制的类,用于断言和错误检查。
  • StringUtil:负责一些字符串的处理,例如路径格式转换等。

实现包扫描

包扫描的目的是遍历项目中指定包路径下的所有类,并将其加载为 Class 对象。下面的代码展示了包扫描的核心实现:

public static Set<Class<?>> getClassSet(String packageName){
    Set<Class<?>> classSet = new HashSet<>();
    try {
        // 获取包名下的资源 URL 枚举
        Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(POINT, SPLIT));
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            if (url != null) {
                String protocol = url.getProtocol();
                if (FILE.equals(protocol)) {
                    // 处理文件协议的路径
                    String packagePath = url.getPath().replaceAll(SPACE, " ");
                    addClass(classSet, packagePath, packageName);
                } else if ("jar".equals(protocol)) {
                    // 处理 JAR 包中的类文件
                    JarURLConnection jarUrlConnection = (JarURLConnection)url.openConnection();
                    if (jarUrlConnection != null) {
                        JarFile jarFile = jarUrlConnection.getJarFile();
                        if (jarFile != null) {
                            Enumeration<JarEntry> jarEntries = jarFile.entries();
                            while (jarEntries.hasMoreElements()) {
                                JarEntry jarEntry = jarEntries.nextElement();
                                String jarEntryName = jarEntry.getName();
                                if (jarEntryName.endsWith(CLASS_SUFFIX)) {
                                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(POINT)).replaceAll(SPLIT,POINT);
                                    doAddClass(classSet, className);
                                }
                            }
                        }
                    }
                }
            }
        }
    } catch (Exception e) {
        log.warn("get class set failure", e);
        throw new RuntimeException(e);
    }
    return classSet;
}

代码解析

  1. URL 资源获取:首先,通过类加载器获取指定包路径下的所有资源 URL,将它们存储在 Enumeration 中。

  2. 协议判断:根据 URL 的协议(如 filejar),处理不同类型的资源路径。

    • File 协议:对于文件协议,获取包路径并替换特殊字符,然后调用 addClass() 方法将扫描到的类加入集合。

    • Jar 协议:对于 JAR 包,利用 JarURLConnection 连接 JAR 文件并遍历其中的条目 (JarEntry),找到以 .class 结尾的文件,并加载其类名。

  3. 异常处理:在资源扫描过程中,若遇到异常,记录警告日志并抛出运行时异常。

这个实现较为简单,逻辑直接且易于理解,主要涉及文件路径和类名的处理。由于包扫描功能相对单一,代码实现上较为固定化。

小结

这个包扫描模块是 mini-SSM 的核心基础之一。通过简单地实现,我们可以深入理解包扫描的基本流程以及类加载的原理。对包扫描机制感兴趣的同学可以参考项目中的注释,探索更深入的实现细节。