Spring @PropertySource с использованием YAML

Spring Загрузка позволяет нам заменить наши файлы application.properties эквивалентами YAML. Однако я, кажется, попал в ловушку с моими испытаниями. Если я аннотирую my TestConfiguration (простую конфигурацию Java), он ожидает файл свойств.

Например, это не работает: @PropertySource(value = "classpath:application-test.yml")

Если у меня есть это в моем файле YAML:

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

И я бы использовал эти значения примерно так:

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

Однако в итоге я получаю ошибку и так:

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

Как я могу использовать доброту YAML в своих тестах?

Ответ 1

Spring-boot имеет помощника для этого, просто добавьте

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

в верхней части ваших тестовых классов или абстрактного тестового суперкласса.

Редактировать: я написал этот ответ пять лет назад. Он не работает с последними версиями Spring Boot. Это то, что я делаю сейчас (пожалуйста, переведите Kotlin на Java, если необходимо):

@TestPropertySource(locations=["classpath:application.yml"])
@ContextConfiguration(
        initializers=[ConfigFileApplicationContextInitializer::class]
)

добавляется сверху, затем

    @Configuration
    open class TestConfig {

        @Bean
        open fun propertiesResolver(): PropertySourcesPlaceholderConfigurer {
            return PropertySourcesPlaceholderConfigurer()
        }
    }

к контексту.

Ответ 2

Как уже упоминалось, @PropertySource не загружает файл yaml. В качестве обходного решения загрузите файл самостоятельно и добавьте загруженные свойства в Environment.

Выполнение 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);
    }
  }
}

Добавьте в свой тест свой инициализатор:

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

Ответ 3

@PropertySource поддерживает только файлы свойств (это ограничение от Spring, а не сама загрузка). Не стесняйтесь открывать билет функции в JIRA.

Ответ 4

@PropertySource может быть настроен с помощью factory аргумента. Так что вы можете сделать что-то вроде:

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

Где YamlPropertyLoaderFactory - ваш пользовательский загрузчик свойств:

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

Вдохновленный fooobar.com/questions/2428336/...

Ответ 5

Из Spring Загрузите 1.4, вы можете использовать новую аннотацию @SpringBootTest, чтобы добиться этого легче (и упростить установку интеграции в целом), загрузив ваши интеграционные тесты, используя Spring Поддержка загрузки.

Подробности в Spring Блог.

Насколько я могу судить, это означает, что вы получаете все преимущества Spring Boot внешний вид конфигурации, как и в вашем производственном коде, включая автоматический выбор конфигурации YAML из пути к классам.

По умолчанию эта аннотация будет

... сначала попытайтесь загрузить @Configuration из любых внутренних классов, и если это не сработает, он будет искать ваш основной класс @SpringBootApplication.

но при необходимости вы можете указать другие классы конфигурации.

В этом конкретном случае вы можете объединить @SpringBootTest с @ActiveProfiles( "test" ), а Spring получит вашу конфигурацию YAML при условии, что она будет соответствовать обычным стандартам именования загрузки (т.е. application-test.yml).

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

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

    @Autowired
    private MyBean myBean;

    ...

}

Примечание: SpringRunner.class - это новое имя для SpringJUnit4ClassRunner.class

Ответ 6

Другой вариант - установить spring.config.location через @TestPropertySource:

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

Ответ 7

Подход к загрузке свойств yaml, IMHO может быть выполнен двумя способами:

а. Вы можете поместить конфигурацию в стандартное местоположение - application.yml в корневой каталог пути - обычно src/main/resources, и это свойство yaml должно автоматически загружаться при загрузке Spring с указанным вами именем сглаженного пути.

б. Второй подход является немного более обширным, в основном определяющим класс для хранения ваших свойств следующим образом:

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

По сути, это говорит о том, что загружает файл yaml и заполняет класс DbProperties на основе корневого элемента "db".

Теперь, чтобы использовать его в любом классе, вам нужно будет сделать это:

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}

Любой из этих подходов должен работать для вас с помощью Spring -boot.

Ответ 8

Я нашел обходное решение, используя @ActiveProfiles("test") и добавив файл application-test.yml в src/test/resources.

В итоге получилось так:

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

}

Файл application-test.yml просто содержит свойства, которые я хочу переопределить из application.yml(который можно найти в src/main/resources).

Ответ 9

в тестовом блоке @value имеет некоторые проблемы для свойства вызова из файла свойств, поэтому вы можете использовать среду для замены значения

@Autowired
private  Environment environment;
public  void init(){

    String username=environment.getRequiredProperty("db.username");

 }

чтобы вы могли вызвать свойство сейчас как в производственном, так и в тестовом блоке

Ответ 10

потому что вы не настроили snakeyml. spring boot поставляется с функцией @EnableAutoConfiguration. есть также snakeyml config, когда вы вызываете эту аннотацию.

это мой путь:

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

вот мой тест:

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

public class JaxbTest {
//tests are ommited
}

Ответ 11

Мне нужно было прочитать некоторые свойства в моем коде, и это работает с 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);
}

Ответ 12

Загрузка пользовательского файла yml с несколькими конфигурациями профиля в Spring Загрузка.

1) Добавьте свойство bean с запуском SpringBootApplication следующим образом

@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) Конфигурируйте объект Java pojo следующим образом

@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) Создайте пользовательский yml (и поместите его в путь ресурса следующим образом: YML Имя файла: test-service-config.yml

Например, Config в файле 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

Ответ 13

Усиление ответа Матеуша Бальбуса.

Модифицированный класс YamlFileApplicationContextInitializer котором местоположение YAML определяется для каждого тестового класса. К сожалению, это не работает на тест.

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

Использование:

Создайте подкласс YamlFileApplicationContextInitializer с определенным getResourceLocation() и добавьте этот подкласс в аннотацию @SpringApplicationConfiguration.

Я думаю, что самому тестовому классу проще всего сделать этот конкретный подкласс, как в следующем примере.

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

Ответ 15

Я находился в определенной ситуации, когда не мог загрузить класс @ConfigurationProperties из-за именования свойств файла. В конце концов, единственное, что сработало (спасибо @Mateusz Balbus):

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MyTest.ContextConfiguration.class})
public class MyTest {

    @TestConfiguration
    public static class ContextConfiguration {

        @Autowired
        ApplicationContext applicationContext;

        @Bean
        public ConfigurationPropertiesBean myConfigurationPropertiesBean() throws IOException {
            Resource resource = applicationContext.getResource("classpath:my-properties-file.yml");

            YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
            List<PropertySource<?>> loadedSources = sourceLoader.load("yamlTestProperties", resource);
            PropertySource<?> yamlTestProperties = loadedSources.get(0);
            ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment)applicationContext.getEnvironment();
            configurableEnvironment.getPropertySources().addFirst(yamlTestProperties);

            Binder binder = Binder.get(applicationContext.getEnvironment());
            ConfigurationPropertiesBean configurationPropertiesBean = binder.bind("my-properties-file-prefix", Bindable.of(ConfigurationPropertiesBean.class)).get();
            return configurationPropertiesBean;
        }

    }

    @Autowired
    ConfigurationPropertiesBean configurationPropertiesBean;

    @Test
    public void test() {

        configurationPropertiesBean.getMyProperty();

    }

}

Ответ 16


нет необходимости добавлять, например, YamlPropertyLoaderFactory или YamlFileApplicationContextInitializer.  Вы должны преобразовать свою идею. так же, как общий весенний проект. Вы знаете, не используя конфигурацию Java.

просто *.xml


 выполните этот шаг:

Просто добавьте applicationContext.xml как


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

затем добавьте

@ImportResource({"classpath:applicationContext.xml"})

к вашему ApplicationMainClass

Слава богу, все работает хорошо!