Загрузить специфичные для среды свойства для использования с PropertyPlaceholderConfigurer? - PullRequest
31 голосов
/ 05 февраля 2010

Это кажется довольно распространенной проблемой, но я не нашел никакого консенсуса по поводу лучшего метода, поэтому я задаю вопрос здесь.

Я работаю над Java-приложением из командной строки, используя Spring Batch и Spring. Я использую файл свойств вместе с PropertyPlaceholderConfigurer, но я немного не уверен в лучшем способе обработки файлов свойств для нескольких сред (dev, test и т. Д.). Мой Google использует только программные способы загрузки свойств (то есть в самом коде Java), что не работает для того, что я делаю.

Один из подходов, которые я рассмотрел, - это просто разместить файл свойств каждой среды на сервере и добавить каталог файла в путь к классу с помощью аргумента командной строки, но у меня возникли проблемы с загрузкой файла с использованием этого метода.

Другой метод, который я рассматриваю, состоит в том, чтобы просто включить все файлы свойств в jar-файл и использовать системное свойство или аргумент командной строки для заполнения имени файла свойств во время выполнения, например:

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

Ответы [ 8 ]

11 голосов
/ 05 февраля 2010

По сути, у вас есть готовый 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 вызывает основной контекст, который ссылается на родительский элемент, так что JdbcTemplate может использоваться, как настроено в JdbcPropertyPlaceholderConfigurer.

Пример кода такого типа:

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

}

Вышеприведенное будет затем сконфигурировано в Spring следующим образом (обратите внимание, что свойство order следует после обычных заполнителей с префиксом $):

  <!-- 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) Конечно, если вы находитесь в контейнере веб-приложения, вы просто используете JNDI. Но ты не такой, ты не можешь.

Надеюсь, это поможет!

8 голосов
/ 20 июля 2012

Вы можете использовать <context:property-placeholder location="classpath:${target_env}configuration.properties" /> в вашем Spring XML и настройте ${target_env}, используя аргумент командной строки (-Dtarget_env=test.).

Начиная с Spring 3.1, вы можете использовать <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> и указать значение по умолчанию, что устраняет необходимость устанавливать значение в командной строке.

В случае если опция Maven IS, переменная 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 также будут работать.

7 голосов
/ 20 февраля 2015

Конфигуратор заполнителя свойства Spring - несколько неочевидных вариантов

<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}}: Здесь «режим» определяет режим проекта (окружение) - dev / prod Файл свойств выглядит так:

#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
3 голосов
/ 05 февраля 2010

Я согласен - это не должна быть конфигурация времени сборки, поскольку вы хотите развернуть одну и ту же полезную нагрузку в различных контекстах.

Свойство Locations PropertyPlaceHolderConfigurer может принимать различные типы ресурсов. Также может быть ресурс файловой системы или URL? Таким образом, вы можете указать расположение файла конфигурации для файла на локальном сервере, а затем при каждом запуске он будет работать в режиме, указанном файлом конфигурации на этом сервере. Если у вас есть определенные серверы для определенных режимов работы, это будет работать нормально.

Чтение между строк, хотя кажется, что вы хотите запустить одно и то же приложение в разных режимах на одном и том же сервере. В этом случае я бы предложил передать местоположение файла конфигурации через параметр командной строки. Было бы немного сложно передать это значение в PropertyPlaceHolderConfigurer, но это было бы невозможно.

3 голосов
/ 05 февраля 2010

Способ, которым я обычно делал это в прошлом, заключается в том, чтобы выполнить замену среды (dev / test / prod) некоторым способом во время пакета / развертывания.

Это может либо скопировать правильный файл конфигурации в нужное место на сервере, либо просто связать правильный файл конфигурации в пакете развертывания. Если вы используете 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

Надеюсь, это поможет.

1 голос
/ 25 апреля 2015

Привет после прочтения Spring in Action нашел решение, предоставленное Spring. Профиль или условный: вы можете создать несколько профилей, например. test, dev, prod и т. д.

Spring учитывает два отдельных свойства при определении активных профилей: spring.profiles.active и spring.profiles.default. Если весна.profiles.active устанавливается, то его значение определяет, какие профили активны. Но если весна .profiles.active не установлен, тогда Spring обращается к spring.profiles.default. Если ни spring.profiles.active или spring.profiles.default установлен, то нет активные профили, и создаются только те компоненты, которые не определены как входящие в профиль.

Есть несколько способов установить эти свойства: 1 В качестве параметров инициализации на DispatcherServlet 2 В качестве параметров контекста веб-приложения 3 Как записи JNDI 4 Как переменные среды 5 Как свойства системы JVM 6 Использование аннотации @ActiveProfiles в классе интеграционных испытаний

1 голос
/ 05 февраля 2010

Для подстановки во время сборки я использую свойства сборки Maven для подстановки переменных.Вы можете определить, какие свойства загружать в файле Maven settings.xml, и этот файл может соответствовать конкретной среде.Для производственных свойств, использующих PPC, см. Этот блог

0 голосов
/ 15 августа 2011

Я использую параметр classpath и настраиваю classpath для каждой среды в Jetty. В jetty-maven-plugin вы можете установить каталог для тестовых классов и разместить там ваши тестовые ресурсы.

Для нелокальных сред (тестирование / производство) я использую флаг среды и отправляю соответствующие файлы в папку $ JETTY_HOME / resources (которая встроена в путь к классам Jetty)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...