向影子 2024-09-18 15:25 采纳率: 100%
浏览 41
已结题

java 通过反射找路径下的类,打包后就找不到

困扰好久的问题,求解😭
java 通过反射找路径下的类,在 idea 执行正常, 打包后就找不到,解压 jar 包的话,类是打包进去的

代码如下

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("my.test.m.MyClass");
        // idea 和打 jar 包执行  都能找到类
        System.out.println("==> "+clazz);

        Reflections reflections = new Reflections("my.test.m", new SubTypesScanner(false));
        Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
        // idea 能找到
        // jar 包执行后得到空集合
        System.out.println("==> "+classes);
    }
}

pom 的依赖和 build 是这样

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.15.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.10.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.6.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <parameters>true</parameters>
                    <encoding>UTF-8</encoding>
                    <target>1.8</target>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • 写回答

15条回答 默认 最新

  • 专家-吕布[在线] 2024-09-18 19:54
    关注

    引自当下最先进的AI大模型:ChatGPT o1-preciew,若有帮助,还望采纳。


    您好,您遇到的问题是,在使用 Reflections 库通过反射扫描包下的类时,程序在 IntelliJ IDEA 中运行正常,但在打包成 jar 后无法找到类。这是一个常见的问题,主要原因是 Spring Boot 的可执行 jar(fat jar)打包方式和 Reflections 库的工作机制之间存在兼容性问题。


    问题原因分析:

    1. Spring Boot 可执行 jar 的特殊结构:

      • 嵌套结构: Spring Boot 在打包可执行 jar 时,会将应用的类和资源文件放在 BOOT-INF/classes/ 目录下,将依赖的 jar 包放在 BOOT-INF/lib/ 目录下。这使得 jar 包内部存在嵌套的 jar 结构。
      • 类加载器差异: 在 IDEA 中运行时,类路径是展开的目录结构,Reflections 可以直接扫描文件系统。但在打包后的可执行 jar 中,类和资源被嵌套在 jar 内部,默认的类加载器和 Reflections 的扫描机制无法直接访问这些嵌套的资源。
    2. Reflections 库的限制:

      • 扫描机制: Reflections 库通过扫描类路径上的文件和目录来发现类。当这些资源被打包在嵌套的 jar 中时,默认的扫描器无法找到它们。
      • 不支持嵌套 jar: 默认情况下,Reflections 不支持扫描嵌套在 jar 内的 jar(即 fat jar 中的结构)。

    解决方案:

    为了解决这个问题,需要使 Reflections 能够识别和扫描 Spring Boot 可执行 jar 中的类。有以下几种方法:

    方案一:自定义 Vfs.URLType 处理嵌套的 jar

    通过自定义 Vfs.URLType,使 Reflections 能够处理 Spring Boot 可执行 jar 的嵌套结构。

    步骤:

    1. 添加依赖:

      确保您的项目中包含以下依赖:

      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter</artifactId>
              <version>2.1.15.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>org.reflections</groupId>
              <artifactId>reflections</artifactId>
              <version>0.10.2</version>
          </dependency>
      </dependencies>
      
    2. 注册自定义的 Vfs.URLType:

      在您的代码中添加自定义的 Vfs.URLType,示例如下:

      import org.reflections.Reflections;
      import org.reflections.vfs.Vfs;
      
      public class Main {
          public static void main(String[] args) throws ClassNotFoundException {
              // 注册自定义的 Vfs.URLType
              Vfs.addDefaultURLTypes(new Vfs.UrlType() {
                  @Override
                  public boolean matches(URL url) {
                      return url.getProtocol().equals("jar") && url.getPath().contains("!/BOOT-INF/classes!");
                  }
      
                  @Override
                  public Vfs.Dir createDir(URL url) {
                      try {
                          URLConnection urlConnection = url.openConnection();
                          if (urlConnection instanceof JarURLConnection) {
                              return new ZipDir(((JarURLConnection) urlConnection).getJarFile());
                          } else {
                              String path = url.toExternalForm();
                              path = path.replace("jar:", "");
                              path = path.replace("!/BOOT-INF/classes!", "");
                              File file = new File(path);
                              return new ZipDir(new JarFile(file));
                          }
                      } catch (Exception e) {
                          throw new RuntimeException("Could not create Vfs.Dir from URL " + url, e);
                      }
                  }
              });
      
              Reflections reflections = new Reflections("my.test.m", new SubTypesScanner(false));
              Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
              System.out.println("==> " + classes);
          }
      }
      

      说明:

      • 该自定义的 Vfs.URLType 用于匹配 Spring Boot 可执行 jar 中的 BOOT-INF/classes 目录。
      • 通过重写 matchescreateDir 方法,使 Reflections 能够正确处理嵌套的 jar。

    方案二:使用 Spring 提供的类路径扫描工具

    利用 Spring 的 ClassPathScanningCandidateComponentProvider 进行类扫描,避免使用 Reflections 库。

    步骤:

    1. 修改代码:

      import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
      import org.springframework.core.type.filter.AssignableTypeFilter;
      import org.springframework.core.type.filter.TypeFilter;
      import org.springframework.beans.factory.config.BeanDefinition;
      
      public class Main {
          public static void main(String[] args) throws ClassNotFoundException {
              ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
              TypeFilter filter = new AssignableTypeFilter(Object.class);
              scanner.addIncludeFilter(filter);
      
              Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents("my.test.m");
              for (BeanDefinition beanDefinition : beanDefinitions) {
                  String className = beanDefinition.getBeanClassName();
                  Class<?> clazz = Class.forName(className);
                  System.out.println("Found class: " + clazz);
              }
          }
      }
      

      说明:

      • 使用 Spring 的类路径扫描工具,可以正确处理 Spring Boot 可执行 jar 的嵌套结构。
      • 通过指定包名和类型过滤器,可以找到指定包下的所有类。

    方案三:更新 Reflections 库的配置

    通过更新 Reflections 库的配置,使其能够处理从特定的 ClassLoader 中加载的类。

    步骤:

    1. 修改 Reflections 初始化方式:

      import org.reflections.Reflections;
      import org.reflections.scanners.SubTypesScanner;
      import org.reflections.util.ClasspathHelper;
      import org.reflections.util.ConfigurationBuilder;
      
      public class Main {
          public static void main(String[] args) throws ClassNotFoundException {
              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      
              Reflections reflections = new Reflections(new ConfigurationBuilder()
                      .setScanners(new SubTypesScanner(false))
                      .addClassLoader(classLoader)
                      .addUrls(ClasspathHelper.forClassLoader(classLoader))
              );
      
              Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
              System.out.println("==> " + classes);
          }
      }
      

      说明:

      • 使用 ConfigurationBuilder 来自定义 Reflections 的配置。
      • 添加当前线程的 ClassLoader,并指定要扫描的 URLs。
      • 这种方式有时仍可能无法解决问题,具体取决于打包后的 jar 结构。

    总结与建议:

    • 问题的本质原因 是 Reflections 库无法处理 Spring Boot 可执行 jar 的嵌套结构。
    • 解决思路 是让 Reflections 或替代工具能够正确地识别和扫描嵌套在 jar 内的类。
    • 推荐方案方案二,使用 Spring 提供的类路径扫描工具,因为它与 Spring Boot 的兼容性更好,且更可靠。

    附加说明:

    • 为什么 Class.forName 可以找到类?

      • Class.forName("my.test.m.MyClass") 可以直接加载指定的类名,无需扫描类路径。这是因为类加载器知道如何在嵌套的 jar 中定位特定的类文件。
    • 为什么 Reflections 找不到类?

      • Reflections 需要扫描类路径上的所有文件和目录,以找到匹配的类。当类被嵌套在 jar 内部时,默认的扫描器无法访问这些资源。
    • 使用 Spring 的类扫描工具的优势:

      • Spring 的类扫描工具设计时考虑了 Spring Boot 的特性,能够处理嵌套的 jar 结构。
      • 不需要额外的配置或自定义 Vfs.URLType,代码更简洁。

    希望以上内容能帮助您解决问题!如果您还有其他疑问,欢迎继续提问。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(14条)

报告相同问题?

问题事件

  • 系统已结题 9月26日
  • 已采纳回答 9月18日
  • 赞助了问题酬金15元 9月18日
  • 修改了问题 9月18日
  • 展开全部

悬赏问题

  • ¥15 12864只亮屏 不显示汉字
  • ¥20 三极管1000倍放大电路
  • ¥15 vscode报错如何解决
  • ¥15 前端vue CryptoJS Aes CBC加密后端java解密
  • ¥15 python随机森林对两个excel表格读取,shap报错
  • ¥15 基于STM32心率血氧监测(OLED显示)相关代码运行成功后烧录成功OLED显示屏不显示的原因是什么
  • ¥100 X轴为分离变量(因子变量),如何控制X轴每个分类变量的长度。
  • ¥30 求给定范围的全体素数p的(p-2)/p的连乘积值
  • ¥15 VFP如何使用阿里TTS实现文字转语音?
  • ¥100 需要跳转番茄畅听app的adb命令