2010-02-05 41 views
29

这似乎是一个很常见的问题,但我还没有找到任何关于最佳方法的共识,所以我在这里提出了这个问题。加载用于PropertyPlaceholderConfigurer的环境特定属性?

我正在使用Spring Batch和Spring开发命令行Java应用程序。我使用属性文件以及PropertyPlaceholderConfigurer,但我有点不确定处理多个环境(dev,test等)的属性文件的最佳方式。我的谷歌搜索只会提供加载属性的编程方式(即,在Java代码本身中),这对我所做的并不起作用。

我考虑的一种方法是将每个环境的属性文件放在服务器上,并通过命令行参数将文件的目录添加到类路径中,但是我一直无法使用该方法加载文件。

我考虑的另一种方法是只包括在罐子里的所有属性文件,并使用系统属性或命令行参数,以填补在属性文件的名称在运行时,像这样:

<bean id="propertyConfigurer" 
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="locations"> 
     <list> 
      <value>classpath:job.properties.${env}</value> 
     </list> 
    </property> 
</bean> 

我倾向于后者的解决方案,但我也期待看看是否有更好的方法,我忽略。

我还应该提到,我必须在运行时而不是在构建中进行替换。我被限制使用的过程需要一个单一的构建,将通过环境提升到生产环境,所以我无法使用替换ala Maven或Ant。

回答

2

我同意 - 它不应该是一个构建时间配置,因为您希望将相同的有效内容部署到各种上下文中。

PropertyPlaceHolderConfigurer的位置属性可以采用各种类型的资源。也可以是文件系统资源或网址?因此,您可以将配置文件的位置设置为本地服务器上的文件,然后每当它运行时就会以该服务器上配置文件指定的模式运行。如果你有特定的服务器运行的特定模式,这将工作正常。

尽管看起来您想在同一台服务器上以不同模式运行相同的应用程序,但仍可以在各行之间进行阅读。在这种情况下,我会建议通过命令行参数传递配置文件的位置。将这个值传递给PropertyPlaceHolderConfigurer会有点棘手,但不是不可能的。

3

过去我通常这样做的方式是在包/部署时以某种方式执行环境替换(dev/test/prod)。

可以将正确的配置文件复制到服务器上的正确位置,或者只是将正确的配置文件捆绑到部署包中。如果你使用Ant/Maven,这应该相当简单。你使用哪种构建工具? Ant/Maven,它应该为您提供替换值的能力。

另一种使用PropertyPlaceholderConfigurer的替代方法是SYSTEM_PROPERTIES_MODE_OVERRIDE属性。您可以使用此设置属性文件,你希望通过一个系统属性加载的位置,请参见:

http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html#SYSTEM_PROPERTIES_MODE_OVERRIDE

希望有所帮助。

+0

感谢构建时间建议,但它不适用于我的情况。我已经更新了我的问题以反映这一点;我的意思是最初包含这些信息并且忘记了。我会检查你的其他建议。 – Ickster

+0

我使用Maven作为构建工具。在maven打包时,你如何捆绑正确的配置。 – premcs

1

对于构建时间替换我使用Maven构建属性进行变量替换。您可以确定要在Maven settings.xml文件中加载哪些属性,并且该文件可能特定于该环境。对于使用PPC的产品属性,请参阅blog

+0

谢谢。看到我留给乔恩的评论;它也适用于您的建议。 – Ickster

+0

我想你可能没有阅读我提到的博客。这是您在运行时使用的方法。有了这个,你可以设置一个环境变量来获取你想要的任何属性文件,甚至是那些JAR或WAR之外的文件。 – harschware

+0

我对乔恩评论的结尾说我也会检查他的其他建议;我对你的评论意味着暗示我也会检查你的其他建议。对我来说真的不是很清楚。我会很快阅读你提供的链接。谢谢! – Ickster

9

本质上你有一个完成的JAR,你想放到另一个环境中,并且没有任何修改就可以在运行时选择合适的属性。如果这是正确的,那么以下方法是有效的:

1)依赖用户主目录中的属性文件的存在。

配置PropertyPlaceholderConfigurer引用属性文件外部JAR这样的:

<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="ignoreUnresolvablePlaceholders" value="false"/> 
    <property name="order" value="1"/> 
    <property name="locations"> 
     <list> 
     <!-- User home holds secured information --> 
     <value>file:${user.home}/MyApp/application.properties</value> 
     </list> 
    </property> 
    </bean> 

操作系统将确保application.properties文件的内容,因此只有合适的人可以访问它。由于在第一次运行应用程序时此文件不存在,因此请创建一个简单的脚本,用于在启动时向用户询问临界值(例如,用户名,密码,Hibernate方言等)。为命令行界面提供广泛的帮助和合理的默认值。 2)如果您的应用程序处于受控环境中以便可以看到数据库,那么可以将问题简化为使用上面的技术1)创建基本凭据以在上下文启动期间连接到数据库,然后执行使用通过JDBC读取的值进行替换。您将需要一个两阶段的方法来启动应用程序:第1阶段通过application.properties文件调用父上下文,该文件填充JdbcTemplate和关联的DataSource;阶段2调用引用父代的主要上下文,以便可以像在JdbcPropertyPlaceholderConfigurer中配置一样使用JdbcTemplate。

这种代码的一个例子是这样的:

public class JdbcPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { 

    private Logger log = Logger.getLogger(JdbcPropertyPlaceholderConfigurer.class); 
    private JdbcTemplate jdbcTemplate; 
    private String nameColumn; 
    private String valueColumn; 
    private String propertiesTable; 

    /** 
    * Provide a different prefix 
    */ 
    public JdbcPropertyPlaceholderConfigurer() { 
    super(); 
    setPlaceholderPrefix("#{"); 
    } 

    @Override 
    protected void loadProperties(final Properties props) throws IOException { 
    if (null == props) { 
     throw new IOException("No properties passed by Spring framework - cannot proceed"); 
    } 
    String sql = String.format("select %s, %s from %s", nameColumn, valueColumn, propertiesTable); 
    log.info("Reading configuration properties from database"); 
    try { 
     jdbcTemplate.query(sql, new RowCallbackHandler() { 

     public void processRow(ResultSet rs) throws SQLException { 
      String name = rs.getString(nameColumn); 
      String value = rs.getString(valueColumn); 
      if (null == name || null == value) { 
      throw new SQLException("Configuration database contains empty data. Name='" + name + "' Value='" + value + "'"); 
      } 
      props.setProperty(name, value); 
     } 

     }); 
    } catch (Exception e) { 
     log.fatal("There is an error in either 'application.properties' or the configuration database."); 
     throw new IOException(e); 
    } 
    if (props.size() == 0) { 
     log.fatal("The configuration database could not be reached or does not contain any properties in '" + propertiesTable + "'"); 
    } 
    } 

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 
    this.jdbcTemplate = jdbcTemplate; 
    } 

    public void setNameColumn(String nameColumn) { 
    this.nameColumn = nameColumn; 
    } 

    public void setValueColumn(String valueColumn) { 
    this.valueColumn = valueColumn; 
    } 

    public void setPropertiesTable(String propertiesTable) { 
    this.propertiesTable = propertiesTable; 
    } 

} 

以上将随后在春天像这样被配置(注意顺序财产而来的通常$前缀占位符后):

<!-- Enable configuration through the JDBC configuration with fall-through to framework.properties --> 
    <bean id="jdbcProperties" class="org.example.JdbcPropertyPlaceholderConfigurer"> 
    <property name="ignoreUnresolvablePlaceholders" value="false"/> 
    <property name="order" value="2"/> 
    <property name="nameColumn" value="name"/> 
    <property name="valueColumn" value="value"/> 
    <property name="propertiesTable" value="my_properties_table"/> 
    <property name="jdbcTemplate" ref="configurationJdbcTemplate"/> <!-- Supplied in a parent context --> 
    </bean> 

这将允许在Spring配置

<!-- Read from application.properties --> 
<property name="username">${username}</property> 
... 
<!-- Read in from JDBC as part of second pass after all $'s have been fulfilled --> 
<property name="central-thing">#{name.key.in.db}</property> 

发生后续3)当然,如果你在一个Web应用程序容器中,那么你只需要使用JNDI。但你并不是你所不能的。

希望这会有所帮助!

+0

我认为你对提示初始属性设置的建议很有趣。不幸的是,这不是我可以使用的,因为应用程序将在所有环境中通过调度工具启动,并且我使用的命令字符串将无法从一次运行更改为下一次运行。 – Ickster

+0

请记住,这只是初始安装,需要创建application.properties文件。如果你可以安排一个系统管理员根据模板放置文件,根据需要进行配置,那么这可能会起作用。当然,这确实需要将应用程序部署在已知和受控的环境中。 –

0

我使用类路径选项并调整Jetty中每个环境的类路径。在jetty-maven-plugin中,你可以为testclasses设置一个目录并让你的测试资源位于那里。

非本地环境(测试/生产),我使用的环境标志和适当的文件发送到$ JETTY_HOME /资源文件夹(内置于码头的classpath)

3

This blog post为了切换一些好的想法出属性文件。我最终使用double PropertyPlaceholderConfigurer来使用系统属性来指定配置文件。

8

您可以在Spring XML中使用<context:property-placeholder location="classpath:${target_env}configuration.properties" /> ,并使用命令行参数(-Dtarget_env=test.)配置${target_env}

从Spring 3.1开始,您可以使用<context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" />并指定一个默认值,从而无需在命令行上设置该值。

如果Maven是一个选项,Spring变量可以在插件执行期间设置,例如,在测试或集成测试执行期间。

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.12</version> 
    <configuration> 
     <systemPropertyVariables> 
      <target_env>test.</target_env> 
     </systemPropertyVariables> 
    </configuration> 
</plugin> 

我假设不同的Maven配置文件也可以工作。

6

弹簧特性占位符配置者 - 一些不那么明显的选择

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="location" value="classpath:db.properties"></property> 
</bean> 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
    <property name="url" value="${db.url.${mode}}" /> 
    <property name="username" value="${db.username.${mode}}" /> 
    <property name="password" value="${db.password.${mode}}" /> 
</bean> 

${db.username.${mode}}:这里的 “模式” 定义项目模式(环境) - 开发/生产 属性文件看起来像:

#Database properties 
#mode dev/prod 
mode=dev 

#dev db properties 
db.url.dev=jdbc:mysql://localhost:3306/dbname 
db.username.dev=root 
db.password.dev=root 

#prod db properties 
db.url.prod=jdbc:mysql://localhost:3306/dbname 
db.username.prod=root 
db.password.prod=root 
+0

这对我来说确实是一个不错的选择。 – RishiPandey

+0

我们可以指定系统环境。在模式值中的属性文件中变量。这样我们就不必对可部署的代码做任何改变。我们可以通过改变环境变量来部署任何地方。 – RishiPandey

+0

RishiPandey,你会怎么做?告诉属性文件从系统env变量设置模式值? –

0

嗨读完Spring in Action发现了Spring提供的解决方案。 配置文件或有条件:您可以创建多个配置文件,例如。测试,开发,产品等

当确定哪些配置文件处于活动状态时,Spring会尊重两个独立的属性: spring.profiles.active和spring.profiles.default。如果设置了spring.profiles.active ,则其值将确定哪些配置文件处于活动状态。但是,如果未设置弹簧 .profiles.active,则Spring将查找spring.profiles.default。如果既没有设置 spring.profiles.active也没有设置spring.profiles.default,那么没有 活动配置文件,并且只有那些未定义为在配置文件中的bean才会创建。

有几种方式来设置这些属性: 1作为上的DispatcherServlet 2初始化参数作为Web应用程序的上下文参数 3作为JNDI条目 4如环境变量 5如JVM系统属性 6使用@集成测试类中的ActiveProfiles注释

相关问题