【问题标题】:Is there a way to get, at runtime, the version of Guava in use?有没有办法在运行时获取正在使用的 Guava 版本?
【发布时间】:2011-10-08 02:49:27
【问题描述】:

我对 Guava 源代码和文档进行了快速 grep,但似乎都没有提及版本。我想知道是否有一种方法可以在运行时获取 Guava 的版本信息。

如果实际上不存在这样的东西,则不必通过任何 getter 访问此版本信息;如果它藏在某个字段中,而在加载 Guava 时不会被 GC 处理,那就足够了。

此版本信息在运行时是否可用?


我对此有一个非常具体的用途。我工作的很大一部分是分析 Java 堆转储,以识别和修复代码中导致过度使用内存的地方。对于这项任务,我使用fasthat,这是jhat 的一个经过大量修改的版本,具有对我的工作有用的特殊功能。

其中一项功能是显示容器的内容。我已经为ArrayListHashMapConcurrentHashMap 等实现了这个(根据我在堆转储中遇到的情况,我按需实现了类型打印机。)目前,我正在实现一个Guava 的CustomConcurrentHashMap 的类型打印机。

由于结构的布局可以在版本之间发生变化,我的代码会根据正在使用的版本调整其解包行为。例如,在工作中,我们曾经使用 JRuby 1.2,但最近切换到 JRuby 1.6,所以我为这两个版本都有类型打印机,它会根据在堆转储中找到的版本信息来选择版本。

所以,这就是问题第二段的重点:如果版本信息在堆转储中的任何位置,这就是我所需要的。

在有人问之前:堆转储数据不是“实时”的,所以你不能简单地调用toString 或类似的方法。您确实必须遍历数据结构才能提取位,并且您确实必须使用第 n 级的实现细节。 ;-)

【问题讨论】:

    标签: java guava


    【解决方案1】:

    您可以从 Guava 的 JAR 清单中检索版本。

    public static String getGuavaVersion() {
        try {
            File file = new File(Charsets.class.getProtectionDomain().getCodeSource().getLocation().toURI());
            try (JarFile jar = new JarFile(file)) {
                return jar.getManifest().getMainAttributes().getValue("Bundle-Version");
            }
        } catch (Exception ex) {
            throw new RuntimeException("Unable to get the version of Guava", ex);
        }
    }
    

    不幸的是,这仅适用于 Guava 11+。 Guava 10 和更早的版本还不是 OSGI 包。

    另一个选项是从pom.properties 检索版本。这也适用于旧版本的 Guava:https://gist.github.com/seanizer/8de050427f3f199cf8f085b2a3a2473e

    【讨论】:

      【解决方案2】:

      如果要获取maven构建的类的版本,可以从类入手,找到它来自的jar,读取maven添加的元信息(比如版本)

      如果版本在路径中,更简单的方法是查看您的类路径,从文件名中找到 guava 和您正在使用的版本。

      对于堆转储,类路径位于系统属性中。

      【讨论】:

      • jhat/fasthat 旨在独立加载堆转储文件,无需咨询任何其他依赖文件,如 jar 文件。我更希望版本信息可以是实际堆的一部分,而不是元数据。
      • 类路径将在堆中。
      • 是的,但这很脆弱,因为通常您可以重命名 jar 文件,或重新打包它们(例如 jruby-complete-*.jar,这是一堆 jar 文件的合并)。
      • 尝试从并非设计为存在的堆转储中获取信息可能很脆弱。如果重命名 jar,它仍然是原始版本还是基于它的副本。您可以使用 jarjar 重命名包,并混淆更改类和方法的代码。你已经制定了一些关于你期望能够理解的标准。
      • 显然,如果 Java 包被重命名,那么所有的赌注都没有了。这不是我所说的关于jruby-complete-*.jar 之类的东西。我仍然希望有问题的课程被命名为com.google.common.collect.CustomConcurrentHashMap。我只是不希望它所在的 jar 文件被命名为guava-r09.jar;据我所知,它可能是jruby-complete-1.7.0.jar 的一部分。
      【解决方案3】:

      这更像是一种解决方法,但我想如果没有更简单的方法可以访问 Guava 版本,您可以这样做:

      在大多数 Guava 版本中,添加/删除了类/字段/方法。您可以尝试在堆转储中查找它们,并根据它们的存在确定 Guava 版本。

      类似:

      /**
       * A {@link Predicate} that checks whether a class exists in the given {@link HeapDump}
       */
      public class ClassExistsPredicate implements Predicate<String> {
      
          private final HeapDump heapDump;
      
          public ClassExistsPredicate(HeapDump heapDump) {
              this.heapDump = heapDump;
          }
      
          @Override
          public boolean apply(String fullyQualifiedClassName) {
              // checks whether the given class exists in the heap dump
              return true;
          }
      
      }
      
      
      public enum GuavaVersion {
      
          R01,
      
          R02 {
              @Override
              Set<String> getAddedClasses() {
                  return ImmutableSet.of("com.google.common.base.Foo");
              }
          },
      
          R03 {
              @Override
              Set<String> getAddedClasses() {
                  return ImmutableSet.of("com.google.common.collect.ForwardingFooIterator");
              }
          },
      
          R04 {
              @Override
              Set<String> getAddedClasses() {
                  return ImmutableSet.of("com.google.common.collect.FooFoo2");
              }
          };
      
          /**
           * @return a {@link Set} of added class names that uniquely identify this version from the preceding one (not
           *         necessarily <b>all</b> classes!)
           */
          Set<String> getAddedClasses() {
              return ImmutableSet.of();
          }
      
          public static GuavaVersion getGuavaVersionFor(HeapDump heapDump) {
              ClassExistsPredicate classExists = new ClassExistsPredicate(heapDump);
      
              for (GuavaVersion version : Lists.reverse(Arrays.asList(GuavaVersion.values()))) {
                  if (Iterables.all(version.getAddedClasses(), classExists)) {
                      return version;
                  }
              }
      
              throw new RuntimeException("Unable to determine Guava version...");
          }
      
      }
      

      显然,您应该缓存 Guava 版本号,因为计算可能很慢...可以扩展该方法以考虑添加的方法/字段。

      这种方法也适用于其他项目。

      【讨论】:

      • 我接受了你的回答,因为它最接近问题的精神,尽管我也接受 Peter Lawrey 的观点,即版本测试可能是错误的树。
      猜你喜欢
      • 2019-06-12
      • 2013-01-03
      • 1970-01-01
      • 2020-11-10
      • 2019-05-14
      • 1970-01-01
      • 2016-08-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多