property leer example context application java spring properties

java - leer - Cargar propiedades específicas del entorno para usar con PropertyPlaceholderConfigurer?



spring boot application.properties example (9)

Esto parece ser un problema bastante común, pero no he encontrado ningún tipo de consenso sobre el mejor método, por lo que estoy planteando la pregunta aquí.

Estoy trabajando en una aplicación Java de línea de comandos usando Spring Batch y Spring. Estoy usando un archivo de propiedades junto con PropertyPlaceholderConfigurer, pero no estoy seguro de cuál es la mejor forma de manejar los archivos de propiedades para múltiples entornos (desarrollo, prueba, etc.). Mi Google sólo está apareciendo formas programáticas de cargar las propiedades (es decir, en el código de Java en sí), que no funciona para lo que estoy haciendo.

Un enfoque que he considerado es simplemente colocar el archivo de propiedades de cada entorno en el servidor y agregar el directorio del archivo al classpath a través de un argumento de línea de comandos, pero he tenido problemas para cargar el archivo con ese método.

El otro método que estoy considerando es simplemente incluir todos los archivos de propiedades en el contenedor y usar una propiedad del sistema o un argumento de línea de comando para completar el nombre del archivo de propiedades en el tiempo de ejecución, como este:

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

Me inclino por la última solución, pero también estoy buscando si hay un mejor método que estoy pasando por alto.

También debo mencionar que tengo que hacer la sustitución en tiempo de ejecución en lugar de en la compilación. El proceso que estoy obligado a usar requiere una compilación única que se promocionará a través de los entornos para la producción, por lo que no puedo usar la sustitución de Maven o Ant.


Esencialmente, tiene un JAR terminado que desea colocar en otro entorno y, sin ninguna modificación, haga que seleccione las propiedades adecuadas en tiempo de ejecución. Si eso es correcto, entonces los siguientes enfoques son válidos:

1) Confíe en la presencia de un archivo de propiedades en el directorio de inicio del usuario.

Configure PropertyPlaceholderConfigurer para hacer referencia a un archivo de propiedades externo al JAR como este:

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

El sistema operativo protegerá los contenidos del archivo application.properties para que solo las personas correctas puedan tener acceso a él. Como este archivo no existe cuando primero ejecuta la aplicación, cree un script simple que interrogará al usuario sobre los valores críticos (por ejemplo, nombre de usuario, contraseña, dialecto Hibernate, etc.) al inicio. Proporcionar ayuda extensa y valores predeterminados razonables para la interfaz de línea de comandos.

2) Si su aplicación se encuentra en un entorno controlado para que se pueda ver una base de datos, entonces el problema puede reducirse a crear las credenciales básicas utilizando la técnica 1) anterior para conectarse a la base de datos durante el inicio del contexto y luego realizar la sustitución utilizando valores leídos a través de JDBC. Necesitará un enfoque de dos fases para el inicio de la aplicación: la fase 1 invoca un contexto padre con el archivo application.properties rellenando una JdbcTemplate y el DataSource asociado; la fase 2 invoca el contexto principal que hace referencia al padre para que JdbcTemplate se pueda usar como se configuró en el JdbcPropertyPlaceholderConfigurer.

Un ejemplo de este tipo de código sería este:

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

Lo anterior se configuraría entonces en Spring de esta manera (tenga en cuenta que la propiedad del pedido viene después de los marcadores de posición usuales $ prefijados):

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

Esto permitiría que ocurriera lo siguiente en la configuración de 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) Por supuesto, si estás en un contenedor de aplicaciones web, entonces simplemente usas JNDI. Pero no eres así que no puedes.

¡Espero que esto ayude!


Estoy de acuerdo: no debe ser una configuración de tiempo de compilación, ya que desea implementar la misma carga exacta en los diversos contextos.

La propiedad Locations de PropertyPlaceHolderConfigurer puede tomar varios tipos de recursos. ¿También puede ser un recurso de sistema de archivos o una url? Por lo tanto, podría establecer la ubicación del archivo de configuración en un archivo en el servidor local y, luego, cada vez que se ejecute, se ejecutará en el modo especificado por el archivo de configuración en ese servidor. Si tiene servidores particulares para modos particulares de ejecución, esto funcionaría bien.

Leyendo entre las líneas, aunque parece que desea ejecutar la misma aplicación en diferentes modos en el mismo servidor. Lo que sugeriría en este caso es pasar la ubicación del archivo de configuración a través de un parámetro de línea de comando. Sería un poco difícil pasar este valor al PropertyPlaceHolderConfigurer pero no sería imposible.


Hola, después de leer Spring in Action, encontré una solución provista por Spring. Perfil o condicional: puede crear múltiples perfiles, por ejemplo. prueba, dev, prod, etc.

Spring reconoce dos propiedades separadas cuando determina qué perfiles están activos: spring.profiles.active y spring.profiles.default. Si se establece spring.profiles.active, su valor determina qué perfiles están activos. Pero si Spring .profiles.active no está configurado, Spring ve a spring.profiles.default. Si no se establece spring.profiles.active ni spring.profiles.default, no hay perfiles activos, y solo se crean los beans que no están definidos como pertenecientes a un perfil.

Hay varias formas de establecer estas propiedades: 1 Como parámetros de inicialización en DispatcherServlet 2 Como parámetros de contexto de una aplicación web 3 Como entradas JNDI 4 Como variables de entorno 5 Como propiedades del sistema JVM 6 Usando la anotación @ActiveProfiles en una clase de prueba de integración


La forma en que normalmente hice esto en el pasado es realizar una sustitución del entorno (dev / test / prod) de algún modo en el momento del paquete / implementación.

Eso puede copiar el archivo de configuración correcto en la ubicación correcta en el servidor o simplemente agrupar el archivo de configuración correcto en el paquete de implementación. Si usa Ant / Maven esto debería ser bastante sencillo de lograr. ¿Qué herramienta de compilación estás usando? Ant / Maven, que debería proporcionarle la posibilidad de sustituir un valor.

Otra alternativa, que utiliza PropertyPlaceholderConfigurer es la de la propiedad SYSTEM_PROPERTIES_MODE_OVERRIDE. Puede usar esto para establecer la ubicación del archivo de propiedades que desea cargar a través de una propiedad del sistema, consulte:

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

Espero que ayude.


Para la sustitución en tiempo de construcción, uso las propiedades de construcción de Maven para la sustitución de variables. Puede determinar qué propiedades cargar en su archivo Maven settings.xml y el archivo podría ser específico del entorno. Para las propiedades de producción que usan PPC, vea este blog


Puede usar <context:property-placeholder location="classpath:${target_env}configuration.properties" /> en su Spring XML y configure ${target_env} usando un argumento de línea de comandos ( -Dtarget_env=test. ).

Comenzando en Spring 3.1, puede usar <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> y especificar un valor predeterminado, eliminando así la necesidad de establecer el valor en la línea de comandos .

En caso de que Maven sea una opción, la variable Spring se podría configurar durante la ejecución del complemento, por ejemplo, durante la prueba o la ejecución de la prueba de integración.

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

Asumo que los diferentes perfiles de Maven también funcionarían.


Spring Property Placeholder Configurer - Algunas opciones no tan obvias

<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}} : aquí "mode" define el modo de proyecto (entorno) - dev / prod El archivo de propiedades se ve así:

#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


Utilizo la opción classpath y ajusto el classpath por entorno en Jetty. En el plugin jetty-maven, puede establecer un directorio para clases de prueba y tener allí sus recursos de prueba.

Para entornos no locales (prueba / producción) utilizo un indicador de entorno y envío los archivos apropiados a la carpeta $ JETTY_HOME / resources (que está incorporada en el classpath de Jetty)


Esta entrada de blog tiene algunas buenas ideas para cambiar los archivos de propiedades. Terminé usando el PropertyPlaceholderConfigurer doble para usar una propiedad del sistema para especificar el archivo de configuración.