Java构建过程中更改常量的最佳方法

5
我继承了一个在Tomcat下运行的Java应用程序(servlets)。出于历史原因,代码具有不同的“外观和感觉”选项,这取决于将应用程序部署到何处(基本上是品牌问题)。
有几个常量控制此品牌过程,它们具有不同的功能,不应压缩为单个常量(即BRAND、MULTI-LANGUAGE以及图标和css样式表的位置等)。
目前,开发团队必须手动更改这些常量(它们至少被本地化在一个数据类中并且有良好的文档),然后使用ANT重新编译应用程序。
假设Ant 1.8和Java 6.x至少可用,最佳的自动化此过程的方法是什么?
我知道没有使用编译器参数的好解决方案(例如C或C++中可以做的那样),而且我倾向于编辑包含常量的源文件的某种“最佳方式”,或者将它们放入另一个文件中,并使用ant构建过程进行交换。我希望有一个结果,可以使用类似于“ant build brand-x”的东西工作,其中更改品牌将更改生成的构建。
谢谢,
-理查德
6个回答

9
将您的值放入一个属性文件中,例如“myapp.properties”,然后从类路径中在启动时将它们加载到您的常量中(有关其如何适应构建过程的详细信息,请参见下文):
public class Constants
{
    private static final Properties props = new Properties();
    public static final String MY_CONSTANT;

    static
    {
        InputStream input = Constants.class.getResourceAsStream("/myapp.properties");
        if(input != null)
        {
           try
           {
              properties.load(input);
           }
           catch(IOException e)
           {
              // TODO log error
           }
        }

        // Initialize constants (dont' forget defaults)
        MY_CONSTANT = properties.getProperty("constant", "default");
        // .. other constants ...
    }
}

现在,每个品牌都有一个单独的属性文件。通过-D或build.properties将其名称传递给ANT,并在打包之前将文件复制到构建目录中。显然,上面的代码可以工作,但是您可以通过多种方式对其进行优化,使其更加健壮可靠。

这是一个不错的解决方案,但对于非字符串常量无效(我尝试了几种方法)。这就是为什么我最终选择了不太优雅的 ant 替换方法。-Richard - Huntrods
你不能只是添加一个“private static int getInt(String name, int def)”帮助方法吗?我看不出为什么它不适用于非字符串,至少需要一些额外的代码。 - Dave Ray

2
使用Ant中的replace任务来更改值。

这是暴力方法,但它可以工作。然而,它会覆盖文件 - 所以你必须创建一个保存(或模板)副本,并每次复制一个新的副本。-R - Huntrods
跟进一下...自从2008年这个答案以来,我一直在使用这个确切的方法,它一直运行得非常完美。在开发过程中,我可能每周编译系统20多次,效果非常好。(当不进行开发时,系统可以稳定运行数月之久)。 - Huntrods

1

还有一种“Spring”的方式,就是使用属性文件和一个bean来从属性中获取值,并将它们注入到需要它们的类中,例如:

<bean id="propertyPlaceholder"  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:configuration.properties" />
</bean>

然后,您可以使用“ant-like”语法注入属性:

<bean id="connectionPool"  class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource">
    <property name="databaseName" value="mydb" />
    <property name="url" value="${db.url}" />
    ...

这可能需要比您想象的重写更多。如果您将在每次编译时更改常量,那么我建议您注意此陷阱(如果您使用静态final变量的话)。

public class Foo {
 public static final int SOME_CONSTANT=1;
..
}

public class Bar {
  ...
   int x=5+Foo.SOME_CONSTANT;
  ...
}

如果您将Foo中的SOME_CONSTANT更改为2,但不重新编译Bar,则Bar将保留SOME_CONSTANT的值为1,因为静态常量已经被编译(因为编译器看到它不需要再次计算它们)。

1

我更喜欢使用Ant的expandproperties过滤器,而不是替换任务。使用替换任务时,构建文件往往会变得大多数是标记化。expandproperties允许您直接在文本中嵌入Ant属性。

<copy file="from" tofile="to">
  <filterchain>
    <expandproperties />
  </filterchain>
</copy>

1

我有一个解决方案,针对这个特定的情况可以正常工作。我使用了Ant替换任务和“保存”的常量类版本一起使用:

<target name="one" description="constant substitution #1">
  <delete file="./tme3/MyConst.java" />
  <copy file="./save/MyConst.java" tofile="./tme3/MyConst.java" />
  <replace file="./tme3/MyConst.java" token="@BRANDING@" value="ONE_BRAND"/>
  <replace file="./tme3/MyConst.java" token="@STYLESHEET@"
           value="../stylesheet/onebrand.css"/>
  <replace file="./tme3/MyConst.java" token="@FAVICON@" value="../images/onebrand.ico"/>
  <replace file="./tme3/MyConst.java" token="@SHOW_LANGUAGES@" value="false"/>
</target>

我只需复制此块并更改我需要的情况下的替换内容 - 在我的特定情况下,现在有3个集合,但预计会有更多。

感谢所有人的回复。


0

使用 Ant 属性文件,并使用“-Dbrand=X”进行构建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接