Не злоупотребляйте приватными полями.
Использование рефлексии, как это делается в нескольких ответах, - это то, чего мы могли бы избежать.
Это приносит небольшую ценность здесь, в то время как это имеет много недостатков:
- мы обнаруживаем проблемы с отражением только во время выполнения (например, поля больше не существуют)
- Мы хотим инкапсуляцию, но не непрозрачный класс, который скрывает зависимости, которые должны быть видны, и делает класс более непрозрачным и менее тестируемым.
- это поощряет плохой дизайн. Сегодня вы объявляете
@Value String field
. Завтра вы можете объявить 5
или 10
о них в этом классе, и вы можете даже не знать, что вы уменьшаете дизайн класса. При более наглядном подходе к установке этих полей (например, конструктора) вы дважды подумаете, прежде чем добавлять все эти поля, и, вероятно, вы инкапсулируете их в другой класс и используете @ConfigurationProperties
.
Сделайте свой класс тестируемым как единым, так и интеграционным
Чтобы иметь возможность писать как простые модульные тесты (то есть без работающего контейнера Spring), так и интеграционные тесты для вашего класса компонентов Spring, вы должны сделать этот класс пригодным для использования с Spring или без него.
Запуск контейнера в модульном тесте, когда он не требуется, является плохой практикой, которая замедляет локальные сборки: вы этого не хотите.
Я добавил этот ответ, потому что ни один ответ здесь, кажется, не показывает это различие, и поэтому они систематически полагаются на работающий контейнер.
Поэтому я думаю, что вы должны переместить это свойство, определенное как внутреннее свойство класса:
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
в параметр конструктора, который будет вставлен Spring:
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
Пример юнит-теста
Вы можете создать экземпляр Foo
без Spring и ввести любое значение property
благодаря конструктору:
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
Пример интеграционного теста
Вы можете внедрить свойство в контекст с Spring Boot таким простым способом благодаря properties
атрибуту @SpringBootTest
:
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
Вы можете использовать в качестве альтернативы, @TestPropertySource
но он добавляет дополнительную аннотацию:
@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}
С Spring (без Spring Boot) все должно быть немного сложнее, но поскольку я долгое время не использовал Spring без Spring Boot, я не предпочитаю говорить глупости.
В качестве примечания: если у вас есть много @Value
полей для установки, извлечение их в класс, помеченный с помощью @ConfigurationProperties
, более актуально, потому что мы не хотим конструктор с слишком большим количеством аргументов.