2010-02-05 39 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註釋

相關問題