web-dev-qa-db-fra.com

Charger des propriétés spécifiques à l'environnement à utiliser avec PropertyPlaceholderConfigurer?

Cela semble être un problème assez courant, mais je n'ai trouvé aucun consensus sur la meilleure méthode, donc je pose la question ici.

Je travaille sur une ligne de commande Java utilisant Spring Batch et Spring. J'utilise un fichier de propriétés avec un PropertyPlaceholderConfigurer, mais je ne suis pas certain de la meilleure façon de gérer les fichiers de propriétés pour plusieurs environnements (dev, test, etc.). Mon googling ne propose que des méthodes de chargement des propriétés par programmation (c'est-à-dire dans le Java lui-même), qui ne fonctionne pas). t travailler pour ce que je fais.

Une approche que j'ai envisagée consiste simplement à placer le fichier de propriétés de chaque environnement sur le serveur et à ajouter le répertoire du fichier au chemin de classe via un argument de ligne de commande, mais j'ai eu du mal à charger le fichier à l'aide de cette méthode.

L'autre méthode que j'envisage consiste à simplement inclure tous les fichiers de propriétés dans le pot et à utiliser une propriété système ou un argument de ligne de commande pour remplir le nom du fichier de propriétés au moment de l'exécution, comme ceci:

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

Je penche vers cette dernière solution, mais je cherche également à voir s'il existe une meilleure méthode que j'oublie.

Je dois également mentionner que je dois effectuer la substitution lors de l'exécution plutôt que dans la version. Le processus que je suis contraint d'utiliser nécessite une seule version qui sera promue à travers les environnements jusqu'à la production, donc je ne peux pas utiliser la substitution ala Maven ou Ant.

31
Ickster

Je suis d'accord - cela ne devrait pas être une configuration de temps de construction car vous souhaitez déployer exactement la même charge utile dans les différents contextes.

La propriété Locations de PropertyPlaceHolderConfigurer peut prendre différents types de ressources. Peut également être une ressource de système de fichiers ou une URL? Ainsi, vous pouvez définir l'emplacement du fichier de configuration dans un fichier sur le serveur local, puis chaque fois qu'il s'exécute, il s'exécute dans le mode spécifié par le fichier de configuration sur ce serveur. Si vous avez des serveurs particuliers pour des modes de fonctionnement particuliers, cela fonctionnera bien.

En lisant entre les lignes, il semble que vous souhaitiez exécuter la même application dans différents modes sur le même serveur. Ce que je suggérerais dans ce cas est de passer l'emplacement du fichier de configuration via un paramètre de ligne de commande. Il serait un peu délicat de passer cette valeur dans le PropertyPlaceHolderConfigurer mais ce ne serait pas impossible.

3
Michael Wiles

Essentiellement, vous avez un fichier JAR fini que vous souhaitez déposer dans un autre environnement, et sans aucune modification, récupérez les propriétés appropriées au moment de l'exécution. Si c'est correct, les approches suivantes sont valides:

1) Comptez sur la présence d'un fichier de propriétés dans le répertoire personnel de l'utilisateur.

Configurez le PropertyPlaceholderConfigurer pour référencer un fichier de propriétés externe au JAR comme ceci:

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

Le système d'exploitation sécurisera le contenu du fichier application.properties afin que seules les bonnes personnes puissent y accéder. Étant donné que ce fichier n'existe pas lorsque vous lancez l'application pour la première fois, créez un script simple qui interrogera l'utilisateur pour les valeurs critiques (par exemple, nom d'utilisateur, mot de passe, dialecte Hibernate, etc.) au démarrage. Fournissez une aide complète et des valeurs par défaut raisonnables pour l'interface de ligne de commande.

2) Si votre application se trouve dans un environnement contrôlé afin qu'une base de données soit visible, le problème peut être réduit à celui de la création des informations d'identification de base à l'aide de la technique 1) ci-dessus pour se connecter à la base de données lors du démarrage du contexte, puis effectuer une substitution à l'aide des valeurs lues via JDBC. Vous aurez besoin d'une approche en deux phases pour démarrer une application: la phase 1 invoque un contexte parent avec le fichier application.properties remplissant un JdbcTemplate et la DataSource associée; la phase 2 appelle le contexte principal qui fait référence au parent afin que le JdbcTemplate puisse être utilisé tel que configuré dans le JdbcPropertyPlaceholderConfigurer.

Un exemple de ce type de code serait le suivant:

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

}

Ce qui précède serait alors configuré au printemps comme ceci (notez que la propriété order vient après les espaces réservés préfixés $ habituels):

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

Cela permettrait au suivi de se produire dans la configuration 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) Bien sûr, si vous êtes dans un conteneur d'application Web, vous utilisez simplement JNDI. Mais vous n'êtes pas si vous ne pouvez pas.

J'espère que cela t'aides!

11
Gary Rowe

Vous pouvez utiliser <context:property-placeholder location="classpath:${target_env}configuration.properties" /> dans votre XML Spring et configurez ${target_env} en utilisant un argument de ligne de commande (-Dtarget_env=test.).

À partir de Spring 3.1, vous pouvez utiliser <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> et spécifiez une valeur par défaut, éliminant ainsi la nécessité de définir la valeur sur la ligne de commande.

Dans le cas où Maven IS une option, la variable Spring pourrait être définie pendant l'exécution du plugin, par exemple pendant le test ou l'exécution du test d'intégration.

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

Je suppose que différents profils Maven fonctionneraient également.

8
enno

Configurateur d'espace réservé Spring Property - Quelques options pas si évidentes

<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}}: Ici "mode" définit le mode projet (environnement) - Le fichier de propriétés dev/prod ressemble à:

#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
7
Navrattan Yadav

La façon dont j'ai normalement fait cela dans le passé consiste à effectuer une substitution de l'environnement (dev/test/prod) d'une manière ou d'une autre au moment du package/déploiement.

Cela peut soit copier le fichier de configuration correct au bon emplacement sur le serveur, soit simplement regrouper le fichier de configuration correct dans le package de déploiement. Si vous utilisez Ant/Maven, cela devrait être assez simple à réaliser. Quel outil de construction utilisez-vous? Ant/Maven, qui devrait vous donner la possibilité de substituer une valeur.

Une autre alternative, qui utilise PropertyPlaceholderConfigurer est celle de la propriété SYSTEM_PROPERTIES_MODE_OVERRIDE. Vous pouvez l'utiliser pour définir l'emplacement du fichier de propriétés que vous souhaitez charger via une propriété système, voir:

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

J'espère que cela pourra aider.

3
Jon

Pour la substitution de temps de construction, j'utilise les propriétés de construction Maven pour la substitution de variables. Vous pouvez déterminer les propriétés à charger dans votre fichier Maven settings.xml et le fichier peut être spécifique à l'environnement. Pour les propriétés de production en utilisant PPC voir ceci blog

1
harschware

Salut après avoir lu Spring in Action a trouvé une solution fournie par Spring. Profil ou conditionnel: vous pouvez créer plusieurs profils, par exemple. test, dev, prod etc.

Spring respecte deux propriétés distinctes lors de la détermination des profils actifs: spring.profiles.active et spring.profiles.default. Si spring.profiles.active est défini, sa valeur détermine les profils actifs. Mais si spring .profiles.active n'est pas défini, Spring recherche alors spring.profiles.default. Si ni spring.profiles.active ni spring.profiles.default n'est défini, alors il n'y a pas de profils actifs et seuls les beans qui ne sont pas définis comme étant dans un profil sont créés.

Il existe plusieurs façons de définir ces propriétés: 1 En tant que paramètres d'initialisation sur DispatcherServlet 2 En tant que paramètres de contexte d'une application Web 3 En tant qu'entrées JNDI 4 En tant que variables d'environnement 5 En tant que propriétés du système JVM 6 Utilisation de l'annotation @ActiveProfiles sur une classe de test d'intégration

1
Navrattan Yadav

J'utilise l'option classpath et ajuste le classpath par environnement dans Jetty. Dans le plugin jetty-maven, vous pouvez définir un répertoire pour les classes de test et y placer vos ressources de test.

Pour les environnements non locaux (test/production), j'utilise un indicateur d'environnement et envoie les fichiers appropriés au dossier $ JETTY_HOME/resources (qui est intégré au chemin de classe de Jetty)

0
Mond Raymond