2010-02-05 25 views
29

Điều này có vẻ giống như một vấn đề khá phổ biến, nhưng tôi đã không tìm thấy bất kỳ loại đồng thuận nào về phương pháp tốt nhất, vì vậy tôi đặt ra câu hỏi ở đây.Tải thuộc tính môi trường cụ thể để sử dụng với PropertyPlaceholderConfigurer?

Tôi đang làm việc trên một ứng dụng Java dòng lệnh bằng cách sử dụng Spring Batch và Spring. Tôi đang sử dụng một tập tin thuộc tính cùng với một PropertyPlaceholderConfigurer, nhưng tôi là một chút không chắc chắn về cách tốt nhất để xử lý các tập tin thuộc tính cho nhiều môi trường (dev, thử nghiệm, vv). Googling của tôi chỉ chuyển lên các cách lập trình tải các thuộc tính (tức là, trong chính mã Java), không hoạt động cho những gì tôi đang làm.

Một cách tiếp cận tôi đã xem xét đơn giản là đặt tệp thuộc tính của mỗi môi trường trên máy chủ và thêm thư mục của tệp vào classpath thông qua đối số dòng lệnh, nhưng tôi gặp sự cố khi tải tệp bằng phương thức đó.

Phương pháp khác mà tôi đang xem xét là chỉ cần bao gồm tất cả các tập tin thuộc tính trong jar và sử dụng một đối số tài sản của hệ thống hoặc dòng lệnh để điền vào tên của các thuộc tính tập tin tại thời gian chạy, như thế này:

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

Tôi nghiêng về phía giải pháp sau, nhưng tôi cũng đang tìm cách xem có phương pháp nào tốt hơn mà tôi đang xem hay không.

Tôi cũng nên đề cập rằng tôi phải thực hiện thay thế trong thời gian chạy thay vì trong bản dựng. Quá trình tôi bị ràng buộc để sử dụng đòi hỏi một xây dựng duy nhất sẽ được quảng bá thông qua các môi trường để sản xuất, vì vậy tôi không thể sử dụng thay thế ala Maven hoặc Ant.

Trả lời

2

Tôi đồng ý - nó không phải là cấu hình thời gian xây dựng khi bạn muốn triển khai chính xác cùng một tải trọng cho các ngữ cảnh khác nhau.

Thuộc tính Vị trí của PropertyPlaceHolderConfigurer có thể lấy nhiều loại tài nguyên khác nhau. Cũng có thể là một resouce hệ thống tập tin hoặc một url? Vì vậy, bạn có thể đặt vị trí của tệp cấu hình vào một tệp trên máy chủ cục bộ và sau đó bất cứ khi nào nó chạy nó sẽ chạy trong chế độ được chỉ định bởi tệp cấu hình trên máy chủ đó. Nếu bạn có máy chủ cụ thể cho các chế độ cụ thể của hoạt động này sẽ hoạt động tốt.

Đọc giữa các dòng mặc dù có vẻ như bạn muốn chạy cùng một ứng dụng ở các chế độ khác nhau trên cùng một máy chủ. Những gì tôi sẽ đề nghị trong trường hợp này là để vượt qua vị trí của tập tin cấu hình thông qua một tham số dòng lệnh. Nó sẽ là một chút khôn lanh để vượt qua giá trị này vào PropertyPlaceHolderConfigurer nhưng sẽ không thể.

3

Cách mà tôi thường làm điều này trong quá khứ là thực hiện thay thế môi trường (dev/test/prod) theo cách nào đó tại thời điểm gói/triển khai.

Điều đó có thể sao chép đúng tệp cấu hình vào đúng vị trí trên máy chủ hoặc chỉ bó đúng tệp cấu hình trong gói triển khai. Nếu bạn sử dụng Ant/Maven, điều này sẽ khá đơn giản để đạt được. Bạn đang sử dụng công cụ xây dựng nào? Ant/Maven, sẽ cung cấp cho bạn khả năng thay thế một giá trị.

Một phương án khác, sử dụng PropertyPlaceholderConfigurer là thuộc tính SYSTEM_PROPERTIES_MODE_OVERRIDE. Bạn có thể sử dụng để thiết lập vị trí của các thuộc tính tập tin bạn muốn tải thông qua một hệ thống tài sản, xem:

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

Hy vọng rằng sẽ giúp.

+0

Cảm ơn đề xuất xây dựng, nhưng nó không hoạt động cho trường hợp của tôi. Tôi đã cập nhật câu hỏi của mình để phản ánh điều đó; Tôi có nghĩa là để bao gồm thông tin đó ban đầu và quên. Tôi sẽ kiểm tra gợi ý khác của bạn. – Ickster

+0

Tôi đang sử dụng Maven làm công cụ xây dựng. Làm thế nào bạn sẽ bó các cấu hình bên phải trong khi đóng gói trong maven. – premcs

1

Để thay thế thời gian xây dựng, tôi sử dụng thuộc tính xây dựng Maven để thay thế biến. Bạn có thể xác định những thuộc tính nào cần tải trong tệp tin settings.xml Maven của bạn và tệp có thể là dành riêng cho môi trường. Đối với các thuộc tính sản xuất sử dụng PPC, hãy xem blog

+0

Cảm ơn. Xem bình luận tôi để lại cho Jon; nó cũng áp dụng cho đề xuất của bạn. – Ickster

+0

Tôi nghĩ bạn có thể chưa đọc blog mà tôi đề cập đến. Đây là phương pháp bạn sẽ sử dụng khi chạy. Với đó bạn thiết lập một biến môi trường mà chọn lên bất kỳ tập tin thuộc tính bạn muốn, ngay cả những người bên ngoài JAR hoặc WAR của bạn. – harschware

+0

Kết thúc lời nhận xét của tôi với Jon nói rằng tôi cũng sẽ kiểm tra đề xuất khác của anh ấy; bình luận của tôi với bạn là ngụ ý rằng tôi cũng sẽ kiểm tra đề xuất khác của bạn. Thực sự không rõ ràng về phần của tôi. Tôi sẽ đọc liên kết bạn đã cung cấp ngay. Cảm ơn! – Ickster

9

Về cơ bản bạn có một JAR đã hoàn thành mà bạn muốn thả vào một môi trường khác và không có bất kỳ sửa đổi nào, nó sẽ nhận các thuộc tính thích hợp khi chạy. Nếu đúng, thì các phương pháp sau hợp lệ:

1) Dựa vào sự hiện diện của tệp thuộc tính trong thư mục chính của người dùng.

Cấu hình PropertyPlaceholderConfigurer để tham khảo nộp đơn thuộc tính bên ngoài để JAR như thế này:

<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> 

Hệ điều hành sẽ đảm bảo các nội dung của tập tin application.properties để chỉ đúng người có thể có quyền truy cập vào nó . Vì tệp này không tồn tại khi bạn chạy ứng dụng lần đầu tiên, hãy tạo một tập lệnh đơn giản sẽ thẩm vấn người dùng về các giá trị quan trọng (ví dụ: tên người dùng, mật khẩu, phương ngữ Hibernate, v.v.) khi khởi động. Cung cấp trợ giúp mở rộng và các giá trị mặc định hợp lý cho giao diện dòng lệnh.

2) Nếu ứng dụng của bạn ở trong môi trường được kiểm soát để có thể xem cơ sở dữ liệu thì vấn đề có thể bị giảm xuống một trong số các thông tin cơ bản bằng cách sử dụng kỹ thuật 1) ở trên để kết nối với cơ sở dữ liệu trong khi khởi động ngữ cảnh và sau đó thực hiện thay thế bằng cách sử dụng các giá trị đọc qua JDBC. Bạn sẽ cần một cách tiếp cận 2 pha để ứng dụng khởi động: giai đoạn 1 gọi một bối cảnh cha mẹ với tệp application.properties điền một JdbcTemplate và DataSource liên quan; giai đoạn 2 gọi bối cảnh chính tham chiếu đến cha mẹ để JdbcTemplate có thể được sử dụng như được cấu hình trong JdbcPropertyPlaceholderConfigurer.

Một ví dụ về loại mã này sẽ là:

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; 
    } 

} 

ở trên sau đó sẽ được cấu hình trong mùa xuân như thế này (lưu ý là tài sản để đưa ra sau khi thông thường $ placeholders tiền tố):

<!-- 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> 

này sẽ cho phép tiếp theo xảy ra trong cấu hình 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) Tất nhiên, nếu bạn đang ở trong một container ứng dụng web thì bạn chỉ cần sử dụng JNDI. Nhưng bạn không nên bạn không thể.

Hy vọng điều này sẽ hữu ích!

+0

Tôi nghĩ rằng gợi ý của bạn về việc yêu cầu các thuộc tính ban đầu là thú vị. Thật không may, nó không phải cái gì tôi có thể sử dụng như ứng dụng sẽ được đưa ra thông qua một công cụ lập kế hoạch trong tất cả các môi trường và chuỗi lệnh tôi sử dụng sẽ không thể thay đổi từ một lần chạy tiếp theo. – Ickster

+0

Hãy nhớ rằng đó chỉ là cài đặt ban đầu yêu cầu tệp application.properties được tạo.Nếu bạn có thể sắp xếp với một sysadmin để đặt các tập tin, cấu hình khi chúng cần thiết, dựa trên một mẫu, sau đó có thể làm việc. Tất nhiên, điều đó đòi hỏi rằng ứng dụng đang được triển khai trong một môi trường đã biết và được kiểm soát. –

0

Tôi sử dụng tùy chọn classpath và điều chỉnh classpath cho mỗi môi trường trong Jetty. Trong plugin jetty-maven, bạn có thể thiết lập một thư mục cho các lớp thử nghiệm và có các nguồn testresources của bạn nằm ở đó.

Đối với môi trường không địa phương (thử nghiệm/sản xuất) Tôi sử dụng một lá cờ môi trường và gửi các tập tin thích hợp vào thư mục $ JETTY_HOME/nguồn lực (được xây dựng vào classpath Jetty của)

3

This blog post có một số ý tưởng tốt để chuyển đổi ra tập tin thuộc tính. Tôi đã kết thúc bằng cách sử dụng PropertyPlaceholderConfigurer đôi để sử dụng thuộc tính hệ thống để chỉ định tệp cấu hình.

8

Bạn có thể sử dụng <context:property-placeholder location="classpath:${target_env}configuration.properties" /> trong Spring XML và định cấu hình ${target_env} bằng đối số dòng lệnh (-Dtarget_env=test.).

Bắt đầu từ mùa xuân 3.1, bạn có thể sử dụng <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> và chỉ định giá trị mặc định, do đó loại bỏ sự cần thiết phải đặt giá trị trên dòng lệnh.

Trong trường hợp Maven IS là một tùy chọn, biến Spring có thể được đặt trong khi thực thi plugin, ví dụ: trong khi thực hiện kiểm tra tích hợp hoặc kiểm tra.

<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> 

Tôi cho rằng các cấu hình Maven khác nhau cũng sẽ hoạt động.

6

mùa xuân tài sản Placeholder Configurer - Một vài không lựa chọn như vậy rõ ràng

<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}}: Here "chế độ" xác định chế độ dự án (môi trường) - dev/prod Thuộc tính tập tin trông giống như:

#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

Đây thực sự là một lựa chọn tốt cho tôi. – RishiPandey

+0

chúng tôi ca chỉ định hệ thống env. biến trong tập tin thuộc tính trong giá trị chế độ. Bằng cách này, chúng tôi không phải thực hiện bất kỳ thay đổi nào là mã có thể triển khai. chúng ta có thể triển khai bất cứ đâu chỉ bằng cách thay đổi biến enviornment. – RishiPandey

+0

RishiPandey, bạn sẽ làm như thế nào? cho biết tập tin thuộc tính để thiết lập giá trị chế độ từ biến hệ thống env? –

0

Xin chào sau khi đọc Spring in Action đã tìm thấy một giải pháp do Spring cung cấp. Hồ sơ hoặc có điều kiện: bạn có thể tạo nhiều hồ sơ ví dụ: kiểm tra, dev, sản xuất, vv

Spring vinh danh hai thuộc tính riêng biệt khi xác định cấu hình nào đang hoạt động: spring.profiles.active và spring.profiles.default. Nếu spring.profiles.active được đặt, thì giá trị của nó sẽ xác định cấu hình nào đang hoạt động. Nhưng nếu mùa xuân .profiles.active chưa được đặt thì Spring sẽ đến spring.profiles.default. Nếu không có spring.profiles.active hoặc spring.profiles.default được đặt, thì không có cấu hình hoạt động và chỉ những hạt không được xác định là đang được tạo trong tiểu sử.

Có một số cách để thiết lập các thuộc tính: 1 Như các thông số khởi tạo trên DispatcherServlet 2 Như các thông số bối cảnh của một ứng dụng web 3 Như JNDI entries 4 Như biến môi trường 5 Như system properties JVM 6 Sử dụng @ Chú thích ActiveProfiles trên lớp thử nghiệm tích hợp

Các vấn đề liên quan