java - example - Spring Boot y múltiples archivos de configuración externa
application yml spring boot (8)
Acabo de tener un problema similar y finalmente descubrí la causa: el archivo application.properties tenía los atributos de propiedad y rwx incorrectos. Entonces, cuando tomcat comenzó, el archivo application.properties estaba en la ubicación correcta, pero era propiedad de otro usuario:
$ chmod 766 application.properties
$ chown tomcat application.properties
Tengo varios archivos de propiedades que quiero cargar desde classpath. Hay un conjunto predeterminado en /src/main/resources
que es parte de myapp.jar
. Mi springcontext
espera que los archivos estén en classpath. es decir
<util:properties id="Job1Props"
location="classpath:job1.properties"></util:properties>
<util:properties id="Job2Props"
location="classpath:job2.properties"></util:properties>
También necesito la opción de anular estas propiedades con un conjunto externo. Tengo una carpeta de configuración externa en cwd
. Según la carpeta de configuración de doc de inicio de primavera debe estar en classpath. Pero no está claro desde el documento si solo anula las applicaiton.properties
de applicaiton.properties
desde allí o todas las propiedades en config.
Cuando lo probé, solo application.properties
se recogió y el resto de las propiedades todavía se recogen desde /src/main/resources
. He intentado suministrarlos como una lista separada por comas a spring.config.location
pero el conjunto predeterminado aún no se ha anulado.
¿Cómo puedo hacer que múltiples archivos de configuración externa anulen los predeterminados?
Como solución alternativa, actualmente uso app.config.location
(propiedad específica de la aplicación) que proporciono a través de la línea de comandos. es decir
java -jar myapp.jar app.config.location=file:./config
y cambié mi applicationcontext
para
<util:properties id="Job2Props"
location="{app.config.location}/job2.properties"></util:properties>
Y así es como hago la separación entre file y classpath al cargar la aplicación.
EDICIONES:
//psuedo code
if (StringUtils.isBlank(app.config.location)) {
System.setProperty(APP_CONFIG_LOCATION, "classpath:");
}
Realmente me gustaría no utilizar la solución anterior y tener el resorte anular todos los archivos de configuración externos en el classpath como lo hace para el archivo application.properties
.
Con Spring boot, spring.config.location funciona, solo proporcione archivos de propiedades separados por comas.
mira el código a continuación
@PropertySource(ignoreResourceNotFound=true,value="classpath:jdbc-${spring.profiles.active}.properties")
public class DBConfig{
@Value("${jdbc.host}")
private String jdbcHostName;
}
}
uno puede poner la versión predeterminada de jdbc.properties dentro de la aplicación. Las versiones externas se pueden establecer mentira esto.
java -jar target/myapp.jar --spring.config.location=classpath:file:///C:/Apps/springtest/jdbc.properties,classpath:file:///C:/Apps/springtest/jdbc-dev.properties
En función del valor del perfil establecido mediante la propiedad spring.profiles.active, se recuperará el valor de jdbc.host. Entonces cuando (en windows)
set spring.profiles.active=dev
jdbc.host tomará valor de jdbc-dev.properties.
para
set spring.profiles.active=default
jdbc.host tomará valor de jdbc.properties.
Cuando se utiliza Spring Boot, las propiedades se cargan en el siguiente orden (consulte Configuración externa en la guía de referencia Spring Boot).
- Argumentos de línea de comando.
- Propiedades del sistema Java (System.getProperties ()).
- Variables de entorno del sistema operativo.
- Atributos JNDI de java: comp / env
- Un RandomValuePropertySource que solo tiene propiedades al azar. *.
- Propiedades de aplicación fuera de su jar empaquetado (application.properties incluyendo YAML y variantes de perfil).
- Propiedades de aplicación empaquetadas dentro de su jar (application.properties incluyendo YAML y variantes de perfil).
- Anotaciones @PropertySource en tus clases @Configuration.
- Propiedades predeterminadas (especificadas utilizando SpringApplication.setDefaultProperties).
Al resolver propiedades (es decir, @Value("${myprop}")
resolución se realiza en el orden inverso (empezando por 9).
Para agregar diferentes archivos, puede usar las propiedades spring.config.location
, que toma una lista separada por comas de archivos de propiedades o ubicaciones de archivos (directorios).
-Dspring.config.location=your/config/dir/
El de arriba agregará un directorio que será consultado para application.properties
archivos application.properties
.
-Dspring.config.location=classpath:job1.properties,classpath:job2.properties
Esto agregará el archivo de 2 propiedades a los archivos que están cargados.
Los archivos y las ubicaciones de configuración predeterminadas se cargan antes que los de la spring.config.location
adicionales, lo que significa que este último siempre anulará las propiedades establecidas en las anteriores. (Consulte también here de la Guía de referencia de arranque de primavera).
Si
spring.config.location
contiene directorios (a diferencia de los archivos) deben terminar en / (y se agregarán con los nombres generados desdespring.config.name
antes de cargarse). La vía declasspath:,classpath:/config,file:,file:config/
búsqueda por defectoclasspath:,classpath:/config,file:,file:config/
siempre se utiliza, independientemente del valor despring.config.location
. De esa manera, puede configurar valores predeterminados para su aplicación enapplication.properties
(o cualquier otro nombre de base que elija conspring.config.name
) y anularlo en tiempo de ejecución con un archivo diferente, manteniendo los valores predeterminados.
Eche un vistazo al PropertyPlaceholderConfigurer, me parece más claro de usar que la anotación.
p.ej
@Configuration
public class PropertiesConfiguration {
@Bean
public PropertyPlaceholderConfigurer properties() {
final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
// ppc.setIgnoreUnresolvablePlaceholders(true);
ppc.setIgnoreResourceNotFound(true);
final List<Resource> resourceLst = new ArrayList<Resource>();
resourceLst.add(new ClassPathResource("myapp_base.properties"));
resourceLst.add(new FileSystemResource("/etc/myapp/overriding.propertie"));
resourceLst.add(new ClassPathResource("myapp_test.properties"));
resourceLst.add(new ClassPathResource("myapp_developer_overrides.properties")); // for Developer debugging.
ppc.setLocations(resourceLst.toArray(new Resource[]{}));
return ppc;
}
He encontrado que este es un patrón útil a seguir:
@RunWith(SpringRunner)
@SpringBootTest(classes = [ TestConfiguration, MyApplication ],
properties = [
"spring.config.name=application-MyTest_LowerImportance,application-MyTest_MostImportant"
,"debug=true", "trace=true"
]
)
Aquí anulamos el uso de "application.yml" para usar "application-MyTest_LowerImportance.yml" y también "application-MyTest_MostImportant.yml"
(Spring también buscará archivos .properties)
También se incluyen como bonificación extra las configuraciones de depuración y rastreo, en una línea separada para que pueda comentarlas si es necesario;]
La depuración / rastreo es increíblemente útil ya que Spring vaciará los nombres de todos los archivos que carga y los que intenta cargar.
Verá líneas como esta en la consola en tiempo de ejecución:
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_MostImportant.properties'' (file:./config/application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_MostImportant.xml'' (file:./config/application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_MostImportant.yml'' (file:./config/application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_MostImportant.yaml'' (file:./config/application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_LowerImportance.properties'' (file:./config/application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_LowerImportance.xml'' (file:./config/application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_LowerImportance.yml'' (file:./config/application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_LowerImportance.yaml'' (file:./config/application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_MostImportant.properties'' (file:./application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_MostImportant.xml'' (file:./application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_MostImportant.yml'' (file:./application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_MostImportant.yaml'' (file:./application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_LowerImportance.properties'' (file:./application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_LowerImportance.xml'' (file:./application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_LowerImportance.yml'' (file:./application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./application-MyTest_LowerImportance.yaml'' (file:./application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_MostImportant.properties'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_MostImportant.xml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_MostImportant.yml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_MostImportant.yaml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_LowerImportance.properties'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_LowerImportance.xml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_LowerImportance.yml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/config/application-MyTest_LowerImportance.yaml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_MostImportant.properties'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_MostImportant.xml'' resource not found
DEBUG 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Loaded config file ''file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_MostImportant.yml'' (classpath:/application-MyTest_MostImportant.yml)
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_MostImportant.yaml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_LowerImportance.properties'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_LowerImportance.xml'' resource not found
DEBUG 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Loaded config file ''file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_LowerImportance.yml'' (classpath:/application-MyTest_LowerImportance.yml)
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''classpath:/application-MyTest_LowerImportance.yaml'' resource not found
TRACE 93941 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped config file ''file:./config/application-MyTest_MostImportant-test.properties'' (file:./config/application-MyTest_MostImportant-test.properties) resource not found
Spring Boots nos permite escribir diferentes perfiles para escribir en diferentes entornos, por ejemplo, podemos tener archivos de propiedades separados para producción, qa y entornos locales
El archivo application-local.properties con configuraciones según mi máquina local es
spring.profiles.active=local
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=users
spring.data.mongodb.username=humble_freak
spring.data.mongodb.password=freakone
spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
rabbitmq.publish=true
Del mismo modo, podemos escribir application-prod.properties y application-qa.properties tantos archivos de propiedades como queramos
luego escriba algunos scripts para iniciar la aplicación para diferentes entornos, por ejemplo
mvn spring-boot:run -Drun.profiles=local
mvn spring-boot:run -Drun.profiles=qa
mvn spring-boot:run -Drun.profiles=prod
Yo tuve el mismo problema. Quería tener la capacidad de sobrescribir un archivo de configuración interno al inicio con un archivo externo, similar a la aplicación Spring Boot. Detección de propiedades. En mi caso, es un archivo user.properties donde se almacenan los usuarios de mis aplicaciones.
Mis requisitos:
Cargue el archivo desde las siguientes ubicaciones (en este orden)
- El camino de clases
- Subdirectorio A / config del directorio actual.
- El directorio actual
- Desde el directorio o una ubicación de archivo dada por un parámetro de línea de comando al inicio
Se me ocurrió la siguiente solución:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;
import static java.util.Arrays.stream;
@Configuration
public class PropertiesConfig {
private static final Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
private final static String PROPERTIES_FILENAME = "user.properties";
@Value("${properties.location:}")
private String propertiesLocation;
@Bean
Properties userProperties() throws IOException {
final Resource[] possiblePropertiesResources = {
new ClassPathResource(PROPERTIES_FILENAME),
new PathResource("config/" + PROPERTIES_FILENAME),
new PathResource(PROPERTIES_FILENAME),
new PathResource(getCustomPath())
};
// Find the last existing properties location to emulate spring boot application.properties discovery
final Resource propertiesResource = stream(possiblePropertiesResources)
.filter(Resource::exists)
.reduce((previous, current) -> current)
.get();
final Properties userProperties = new Properties();
userProperties.load(propertiesResource.getInputStream());
LOG.info("Using {} as user resource", propertiesResource);
return userProperties;
}
private String getCustomPath() {
return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + PROPERTIES_FILENAME;
}
}
Ahora la aplicación usa el recurso de ruta de clase, pero también busca un recurso en las otras ubicaciones especificadas. El último recurso que existe será recogido y utilizado. Puedo iniciar mi aplicación con java -jar myapp.jar --properties.location = / directory / myproperties.properties para usar una ubicación de propiedades que flote mi bote.
Aquí hay un detalle importante: use un String vacío como valor predeterminado para la propiedad properties.location en la anotación @Value para evitar errores cuando la propiedad no está configurada.
La convención para una ubicación de propiedades es: Usar un directorio o una ruta a un archivo de propiedades como properties.location.
Si desea sobrescribir solo propiedades específicas, se puede usar un Fail de Propiedades con setIgnoreResourceNotFound (verdadero) con el conjunto de recursos establecido como ubicaciones.
Estoy seguro de que esta solución se puede extender para manejar archivos múltiples ...
EDITAR
Aquí mi solución para múltiples archivos :) Como antes, esto se puede combinar con un PropertiesFactoryBean.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
@Configuration
class PropertiesConfig {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
private final static String[] PROPERTIES_FILENAMES = {"job1.properties", "job2.properties", "job3.properties"};
@Value("${properties.location:}")
private String propertiesLocation;
@Bean
Map<String, Properties> myProperties() {
return stream(PROPERTIES_FILENAMES)
.collect(toMap(filename -> filename, this::loadProperties));
}
private Properties loadProperties(final String filename) {
final Resource[] possiblePropertiesResources = {
new ClassPathResource(filename),
new PathResource("config/" + filename),
new PathResource(filename),
new PathResource(getCustomPath(filename))
};
final Resource resource = stream(possiblePropertiesResources)
.filter(Resource::exists)
.reduce((previous, current) -> current)
.get();
final Properties properties = new Properties();
try {
properties.load(resource.getInputStream());
} catch(final IOException exception) {
throw new RuntimeException(exception);
}
LOG.info("Using {} as user resource", resource);
return properties;
}
private String getCustomPath(final String filename) {
return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + filename;
}
}
este es un enfoque simple usando arranque de primavera
TestClass.java
@Configuration
@Profile("one")
@PropertySource("file:/{selected location}/app.properties")
public class TestClass {
@Autowired
Environment env;
@Bean
public boolean test() {
System.out.println(env.getProperty("test.one"));
return true;
}
}
el contexto de app.properties , en la ubicación seleccionada
test.one = 1234
su aplicación de arranque de primavera
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(testApplication.class, args);
}
}
y el contexto predefinido de application.properties
spring.profiles.active = one
puede escribir tantas clases de configuración como desee y habilitarlas / deshabilitarlas simplemente configurando spring.profiles.active = el nombre / nombres del perfil {separados por comas}
como puede ver, la bota de primavera es genial, solo necesita familiarizarse con ella, vale la pena mencionar que también puede usar @Value en sus campos
@Value("${test.one}")
String str;