web-dev-qa-db-fra.com

Spring @PropertySource utilisant YAML

Spring Boot nous permet de remplacer nos fichiers application.properties par des équivalents YAML. Cependant, il me semble avoir un problème avec mes tests. Si j'annote ma TestConfiguration (une configuration Java simple), elle attend un fichier de propriétés.

Par exemple, cela ne fonctionne pas: @PropertySource(value = "classpath:application-test.yml")

Si j'ai ceci dans mon fichier YAML:

db:
  url: jdbc:Oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword

Et j'utiliserais ces valeurs avec quelque chose comme ceci:

@Value("${db.username}") String username

Cependant, je me retrouve avec une erreur comme celle-ci:

Could not resolve placeholder 'db.username' in string value "${db.username}"

Comment puis-je tirer parti de la qualité YAML dans mes tests?

68
checketts

Spring-Boot a un assistant pour cela, il suffit d'ajouter

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

au sommet de vos classes de test ou une superclasse de test abstraite.

41
Ola Sundell

Comme il a été mentionné, @PropertySource ne charge pas le fichier yaml. Pour résoudre ce problème, chargez le fichier vous-même et ajoutez les propriétés chargées à Environment.

Implémentation ApplicationContextInitializer:

public class YamlFileApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    try {
        Resource resource = applicationContext.getResource("classpath:file.yml");
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
        applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
  }
}

Ajoutez votre initialiseur à votre test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = YamlFileApplicationContextInitializer.class)
public class SimpleTest {
  @Test
  public test(){
    // test your properties
  }
}
40
Mateusz Balbus

@PropertySource ne supporte que les fichiers de propriétés (c'est une limitation de Spring, pas de Boot elle-même) N'hésitez pas à ouvrir un ticket de demande de fonctionnalité dans JIRA .

23
Dave Syer

À partir de Spring Boot 1.4, vous pouvez utiliser la nouvelle annotation @SpringBootTest pour y parvenir plus facilement (et pour simplifier la configuration de vos tests d'intégration en général) en amorçant vos tests d'intégration à l'aide du support Spring Boot.

Détails sur le Spring Blog .

Autant que je sache, cela signifie que vous bénéficiez de tous les avantages de la qualité externalized config de Spring Boot, exactement comme dans votre code de production, y compris la récupération automatique de la configuration YAML à partir du chemin de classe.

Par défaut, cette annotation sera 

... tentez d'abord de charger @Configuration à partir de n'importe quelle classe interne, et si cela échoue, il cherchera votre classe primaire @SpringBootApplication.

mais vous pouvez spécifier d'autres classes de configuration si nécessaire.

Dans ce cas particulier, vous pouvez combiner @SpringBootTest avec @ActiveProfiles( "test" ) et Spring récupérera votre configuration YAML, à condition qu’elle respecte les normes de dénomination de démarrage normales (c.-à-d. application-test.yml).

@RunWith( SpringRunner.class )
@SpringBootTest
@ActiveProfiles( "test" )
public class SpringBootITest {

    @Value("${db.username}")
    private String username;

    @Autowired
    private MyBean myBean;

    ...

}

Remarque: SpringRunner.class est le nouveau nom pour SpringJUnit4ClassRunner.class

14
moogpwns

@PropertySource peut être configuré avec l'argument factory. Ainsi, vous pouvez faire quelque chose comme:

@PropertySource(value = "classpath:application-test.yml", factory = YamlPropertyLoaderFactory.class)

YamlPropertyLoaderFactory est votre chargeur de propriétés personnalisé:

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
    }
}

Inspiré par https://stackoverflow.com/a/45882447/4527110

L’approche pour charger les propriétés de yaml, à mon humble avis, peut être réalisée de deux manières:

une. Vous pouvez placer la configuration dans un emplacement standard - application.yml dans la racine du chemin de classe - généralement src/main/resources et cette propriété yaml devrait automatiquement être chargée par Spring Boot avec le nom de chemin aplati que vous avez mentionné.

b. La seconde approche est un peu plus étendue, définissez en gros une classe pour contenir vos propriétés de la manière suivante:

@ConfigurationProperties(path="classpath:/appprops.yml", name="db")
public class DbProperties {
    private String url;
    private String username;
    private String password;
...
}

En gros, cela signifie que vous devez charger le fichier yaml et renseigner la classe DbProperties en fonction de l’élément racine de "db".

Maintenant, pour l'utiliser dans n'importe quelle classe, vous devrez le faire:

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}

Chacune de ces approches devrait fonctionner pour vous proprement en utilisant Spring-boot.

10
Biju Kunjummen

Une autre option consiste à définir le spring.config.location à @TestPropertySource:

@TestPropertySource(properties = { "spring.config.location = classpath:<path-to-your-yml-file>" }
7
PomCompot

J'ai trouvé une solution de contournement en utilisant @ActiveProfiles("test") et en ajoutant un fichier application-test.yml à src/test/resources.

Cela a fini par ressembler à ceci:

@SpringApplicationConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)
@ActiveProfiles("test")
public abstract class AbstractIntegrationTest extends AbstractTransactionalJUnit4SpringContextTests {

}

Le fichier application-test.yml contient uniquement les propriétés que je souhaite remplacer par application.yml (que vous pouvez trouver dans src/main/resources).

3
Poly

Vous pouvez configurer @PropertySource pour charger des fichiers YAML en fournissant une classe dans l'attribut factory. J'ai implémenté une version qui peut également prendre en compte les profils actifs de votre déploiement :).

https://medium.com/@james.tran/how-to-read-profile-based-yaml-configurations-with-propertysource-8131c16b3fc6

0
Mr.J4mes

J'avais besoin de lire certaines propriétés dans mon code et cela fonctionne avec spring-boot 1.3.0.RELEASE

@Autowired
private ConfigurableListableBeanFactory beanFactory;

// access a properties.yml file like properties
@Bean
public PropertySource properties() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("properties.yml"));
    propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
    // properties need to be processed by beanfactory to be accessible after
    propertySourcesPlaceholderConfigurer.postProcessBeanFactory(beanFactory);
    return propertySourcesPlaceholderConfigurer.getAppliedPropertySources().get(PropertySourcesPlaceholderConfigurer.LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME);
}
0
U.V.

il n'est pas nécessaire d'ajouter comme YamlPropertyLoaderFactory ou YamlFileApplicationContextInitializer . Vous devriez convertir votre idée. tout comme un projet de printemps commun. vous savez, n'utilisez pas la configuration Java. 

juste * .xml


suivez cette étape:

Ajoutez simplement applicationContext.xml comme 


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
       default-autowire="byName">

    <context:property-placeholder location="classpath*:*.yml"/>
</beans>

puis ajouter 

@ImportResource({"classpath:application*.xml"})

à votre ApplicationMainClass

Tout fonctionne bien!

0
Forest10

c'est parce que vous n'avez pas configuré snakeyml. spring boot vient avec la fonctionnalité @EnableAutoConfiguration . il y a aussi la configuration snakeyml lorsque vous appelez cette annotation.

c'est mon chemin:

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

voici mon test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(
        classes = {
                AppContextTest.class,
                JaxbConfiguration.class,
        }
)

public class JaxbTest {
//tests are ommited
}
0
user2582794

Améliorer Réponse de Mateusz Balbus .

Classe YamlFileApplicationContextInitializer modifiée où l'emplacement YAML est défini par classe de test. Cela ne fonctionne pas par test, malheureusement.

public abstract class YamlFileApplicationContextInitializer
  implements ApplicationContextInitializer<ConfigurableApplicationContext> {

  /***
   * Return location of a YAML file, e.g.: classpath:file.yml
   *
   * @return YAML file location
   */
  protected abstract String getResourceLocation();

  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    try {
        Resource resource = applicationContext.getResource(getResourceLocation());
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
        applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
  }
}

Usage:

Créez une sous-classe de YamlFileApplicationContextInitializer avec la méthode getResourceLocation() définie et ajoutez cette sous-classe à l'annotation @SpringApplicationConfiguration.

Je pense qu'il est plus facile de rendre la classe de test elle-même cette sous-classe spécifique comme dans l'exemple suivant.

@RunWith(SpringRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = SimpleTest.class)
public class SimpleTest extends YamlFileApplicationContextInitializer {

  @Override
  protected String getResourceLocation() {
    return "classpath:test_specific.yml";
  }

  @Test
  public test(){
    // test your properties
  }
}
0
Michal Foksa

Chargement du fichier yml personnalisé avec la configuration de plusieurs profils dans Spring Boot. 

1) Ajoutez le bean de propriété avec SpringBootApplication au démarrage comme suit

@SpringBootApplication
@ComponentScan({"com.example.as.*"})
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Bean
    @Profile("dev")
    public PropertySourcesPlaceholderConfigurer propertiesStage() {
        return properties("dev");
    }

    @Bean
    @Profile("stage")
    public PropertySourcesPlaceholderConfigurer propertiesDev() {
        return properties("stage");
    }

    @Bean
    @Profile("default")
    public PropertySourcesPlaceholderConfigurer propertiesDefault() {
        return properties("default");

    }
   /**
    * Update custom specific yml file with profile configuration.
    * @param profile
    * @return
    */
    public static PropertySourcesPlaceholderConfigurer properties(String profile) {
       PropertySourcesPlaceholderConfigurer propertyConfig = null;
       YamlPropertiesFactoryBean yaml  = null;

       propertyConfig  = new PropertySourcesPlaceholderConfigurer();
       yaml = new YamlPropertiesFactoryBean();
       yaml.setDocumentMatchers(new SpringProfileDocumentMatcher(profile));// load profile filter.
       yaml.setResources(new ClassPathResource("env_config/test-service-config.yml"));
       propertyConfig.setProperties(yaml.getObject());
       return propertyConfig;
    }
}

2) Configurez l'objet Java pojo comme suit

@Component
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
@ConfigurationProperties(prefix = "test-service")
public class TestConfig {

    @JsonProperty("id") 
    private  String id;

    @JsonProperty("name")
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   

}

3) Créez le fichier yml personnalisé (et placez-le sous le chemin de la ressource, comme suit:

Par exemple, Config dans le fichier yml.

test-service: 
    id: default_id
    name: Default application config
---
spring:
  profiles: dev

test-service: 
  id: dev_id
  name: dev application config

--- 
spring:
  profiles: stage

test-service: 
  id: stage_id
  name: stage application config
0