You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在运行时类路径中查找简单类名匹配指定正则表达式的所有Java类

如何在运行时类路径中查找匹配正则的Java类

刚好我之前折腾过类似的需求,要在运行时扫遍类路径找符合条件的类,其实核心就是遍历类路径资源 + 解析类名 + 正则匹配这几步,下面给你详细拆解:

核心思路

Java的类路径可能包含目录、Jar包(甚至War包),我们需要逐个遍历这些资源:

  • 对于目录:递归扫描所有.class文件,把文件路径转换成全限定类名
  • 对于Jar包:读取Jar内的所有条目,筛选出.class文件后转换为全限定类名
  • 拿到类名后,提取简单类名(全限定名最后一个.后的部分),用正则表达式匹配,符合条件的就可以加载成Class对象或者保留全限定名

具体实现步骤 & 代码示例

1. 封装核心逻辑的工具类

下面是一个可直接复用的工具类,包含类路径遍历、正则匹配、类加载的完整功能:

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassFinder {
    // 用于匹配简单类名的正则实例
    private final Pattern classNamePattern;

    public ClassFinder(String regex) {
        this.classNamePattern = Pattern.compile(regex);
    }

    // 查找所有符合条件的类的全限定名
    public List<String> findMatchingClassNames() throws IOException, ClassNotFoundException {
        List<String> matchingClasses = new ArrayList<>();
        // 使用当前线程的上下文类加载器,适配Web应用等复杂场景
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        // 获取类路径下的所有资源URL
        Enumeration<URL> resources = classLoader.getResources("");
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            String protocol = resource.getProtocol();

            if ("file".equals(protocol)) {
                // 处理目录类型的类路径
                scanDirectory(new File(resource.getPath()), "", matchingClasses);
            } else if ("jar".equals(protocol)) {
                // 处理Jar包类型的类路径,截取Jar文件路径
                String jarPath = resource.getPath().substring(5).split("!")[0];
                scanJar(new JarFile(jarPath), matchingClasses);
            }
        }
        return matchingClasses;
    }

    // 递归扫描目录下的.class文件
    private void scanDirectory(File dir, String packagePrefix, List<String> matchingClasses) throws ClassNotFoundException {
        File[] files = dir.listFiles();
        if (files == null) return;

        for (File file : files) {
            if (file.isDirectory()) {
                // 进入子目录,更新包前缀
                scanDirectory(file, packagePrefix + file.getName() + ".", matchingClasses);
            } else if (file.getName().endsWith(".class")) {
                // 将文件路径转换为全限定类名
                String className = packagePrefix + file.getName().substring(0, file.getName().length() - 6);
                // 提取简单类名并匹配正则
                String simpleName = className.substring(className.lastIndexOf('.') + 1);
                if (classNamePattern.matcher(simpleName).find()) {
                    matchingClasses.add(className);
                    // 如果需要直接获取Class对象,取消下面注释即可
                    // Class<?> clazz = Class.forName(className);
                    // matchingClasses.add(clazz);
                }
            }
        }
    }

    // 扫描Jar包内的.class文件
    private void scanJar(JarFile jarFile, List<String> matchingClasses) throws IOException, ClassNotFoundException {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String entryName = entry.getName();
            if (entryName.endsWith(".class") && !entry.isDirectory()) {
                // 将Jar条目路径转换为全限定类名
                String className = entryName.substring(0, entryName.length() - 6).replace('/', '.');
                // 提取简单类名并匹配正则
                String simpleName = className.substring(className.lastIndexOf('.') + 1);
                if (classNamePattern.matcher(simpleName).find()) {
                    matchingClasses.add(className);
                    // 加载Class对象的话取消注释
                    // Class<?> clazz = Class.forName(className);
                    // matchingClasses.add(clazz);
                }
            }
        }
        jarFile.close();
    }

    // 测试用例
    public static void main(String[] args) throws Exception {
        // 匹配简单类名包含"Cool"的类
        ClassFinder finder = new ClassFinder("Cool");
        List<String> classNames = finder.findMatchingClassNames();
        System.out.println("找到的符合条件的类:");
        for (String name : classNames) {
            System.out.println(name);
            // 加载类并输出Class对象
            Class<?> clazz = Class.forName(name);
            System.out.println("加载完成的Class对象:" + clazz);
        }
    }
}

2. 关键细节说明

  • 类加载器选择:用Thread.currentThread().getContextClassLoader()可以适配Web应用、模块化项目等场景,避免系统类加载器找不到自定义类路径资源的问题
  • 正则灵活调整:示例中是匹配包含指定字符串的简单类名,如果你需要更复杂的规则(比如以Cool开头/结尾),直接修改构造方法传入的正则即可,比如new ClassFinder("^Cool.*")
  • 嵌套Jar处理:上面的代码只处理直接在类路径中的Jar,如果你的项目用了嵌套Jar(比如Spring Boot可执行Jar),需要额外借助JarFile的嵌套读取能力或第三方工具类
  • 性能优化:如果类路径很大,遍历会比较耗时,可以考虑缓存结果,或者只扫描特定包下的类(修改扫描逻辑,只处理指定包前缀的资源)

调用示例

在自己的业务代码中,可以这样快速使用:

// 匹配所有简单类名包含"Cool"的类
ClassFinder finder = new ClassFinder("Cool");
List<String> matchingClassNames = finder.findMatchingClassNames();

// 遍历类名并加载Class对象
for (String className : matchingClassNames) {
    Class<?> clazz = Class.forName(className);
    // 这里可以做你需要的操作,比如实例化、调用方法等
}

内容的提问来源于stack exchange,提问作者Hervian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 18:12:29