有什么办法可以在编译时为Java定义一个常量值

时间:2020-03-06 14:25:36  来源:igfitidea点击:

当我以前用C / C ++编写库时,我习惯于拥有一种返回编译日期/时间的方法。它始终是编译到库中的,因此可以区分库的内部版本。我通过在代码中返回#define来获得此信息:

C ++:

#ifdef _BuildDateTime_
   char* SomeClass::getBuildDateTime() {
      return _BuildDateTime_;
   }
#else
   char* SomeClass::getBuildDateTime() {
      return "Undefined";
   }
#endif

然后在编译时,我在构建脚本中有一个'-D_BuildDateTime_ =Date'。

有什么方法可以用Java来实现这一目标或者类似目标,而无需记住手动编辑任何文件或者分发任何单独的文件。

我从同事那里得到的一个建议是获取ant文件,以在类路径上创建一个文件,并将其打包到JAR中,并由该方法读取它。

诸如此类(假设创建的文件名为" DateTime.dat"):

// I know Exceptions and proper open/closing 
// of the file are not done. This is just 
// to explain the point!
String getBuildDateTime() {
    return new BufferedReader(getClass()
            .getResourceAsStream("DateTime.dat")).readLine();
}

在我看来,这是一种黑客行为,可能会被JAR外部但在类路径上具有类似名称的文件的人绕开/破坏。

无论如何,我的问题是在编译时是否有任何方法可以将常量注入类中

编辑

我之所以考虑在JAR中使用外部生成的文件是一种hack,是因为这是一个库,并且将嵌入在客户端应用程序中。这些客户端应用程序可以定义自己的类加载器,这意味着我不能依赖标准的JVM类加载规则。

我个人更喜欢使用serg10建议的JAR文件中的日期。

解决方案

除非我们想通过C / C ++预处理程序(这是BIG NO-NO)运行Java源代码,否则请使用jar方法。还有其他方法可以从jar中获取正确的资源,以确保有人没有在类路径上放置重复的资源。我们也可以考虑为此使用Jar清单。我的项目完全使用清单来完成我们要尝试执行的操作(带有生成日期,修订,作者等)。

我们将要使用此:

Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");

这将为我们提供类路径上的所有清单。我们可以通过解析URL找出它们可以使用的jar。

AFAIK没有办法用javac做到这一点。这可以通过Ant轻松完成-我将创建一个名为BuildTimestamp.java的一流对象,并在编译时通过Ant目标生成该文件。

这是一个有用的Ant类型。

就我个人而言,我会在运行时加载的jar中寻找一个单独的属性文件...类加载器具有定义的搜索文件顺序,我不记得它是如何工作的,但是我不知道认为在类路径上某处具有相同名称的另一个文件很可能会引起问题。

但是,我们可以执行的另一种方法是在编译它们之前使用Ant将.java文件复制到其他目录中,并在适当时过滤String常量。我们可以使用类似:

public String getBuildDateTime() {
    return "@BUILD_DATE_TIME@";
}

并在Ant文件中编写一个过滤器,以将其替换为build属性。

One suggestion I got from a co-worker
  was to get the ant file to create a
  file on the classpath and to package
  that into the JAR and have it read by
  the method. ... To my mind that's a
  hack and could be circumvented/broken
  by someone having a similarly named
  file outside the JAR, but on the
  classpath.

我不确定让Ant生成文件是否是一个非常可怕的骇客,即使它完全是骇客。为什么不生成属性文件并使用java.util.Properties处理它?

指示清单库版本的更Java风格的方法可能是,如清单文件中所述,在JAR清单中添加版本号。

我记得在一个开源项目中看到过类似的事情:

class Version... {
  public static String tstamp() {
    return "@BUILDTIME@";
  }
}

在模板文件中。使用Ant的过滤副本,我们可以为该宏赋予一个值:

<copy src="templatefile" dst="Version.java" filtering="true">
    <filter token="BUILDTIME" value="${build.tstamp}" />
</copy>

在编译步骤之前,使用它在构建过程中创建Version.java源文件。

我赞成基于标准的方法。将版本信息(以及其他有用的发布者资料,例如内部版本号,Subversion修订号,作者,公司详细信息等)放入jar的清单文件中。

这是一个有据可查并易于理解的Java规范。存在用于创建清单文件的强大工具支持(例如,核心Ant任务或者maven jar插件)。这些可以帮助自动设置一些属性,我已经配置了maven,以便在构建时将jar的maven版本号,Subversion修订版和时间戳记放入清单中。

我们可以使用标准的Java api调用在运行时读取清单的内容,例如:

import java.util.jar.*;

...

JarFile myJar = new JarFile("nameOfJar.jar");    // various constructors available
Manifest manifest = myJar.getManifest();
Map<String,Attributes> manifestContents = manifest.getAttributes();

对我来说,这感觉更像是Java标准方法,因此对于后来的代码维护者来说可能会更容易遵循。