В общем случае рекомендуется внедрять зависимости через конструктор. Но необязательные зависимости рекомендуется внедрять через сеттер.
В этой статье пойдет речь о @Conditional бин.
@Conditional бин
Допустим, в проекте есть необязательный бин, который создается (либо нет) в зависимости от условия: окружения, версии jre, свойства в application.properties.
Например, логгер в production-среде может быть один, а в develepment-среде — другой. Но мы сделаем еще проще — создадим бин Logger, который либо создается, либо нет в зависимости от настройки в application.properties:
logger=true
Для этого аннотируем бин Logger с помощью @ConditionalOnExpression:
@Component @ConditionalOnExpression("${logger:true}") public class Logger { public void log(String message){ System.out.println("log"); } }
Вариантов поставить условия очень много, мы рассматриваем только @ConditionalOnExpression (внутри задается SPEL-выражение), но тут подошел бы и @ConditionalOnProperty:
Итак, выше с помощью аннотации:
@ConditionalOnExpression("${logger:true}")
мы задали, что бин Logger создается только в том случае, если свойство logger=true. Иначе он не создается.
Нежелательно внедрять бин Logger в другой бин AnimalRepository через конструктор AnimalRepository. Потому что внедрение через конструктор подразумевает, что все внедряемые бины уже созданы к моменту вызова конструктора. То есть сначала Logger, потом AnimalRepository. В случае же logger=false бин Logger отсутствует.
Поэтому необязательный бин внедрим через сеттер.
@Autowired(required=false) и внедрение через сеттер
То, что зависимость не обязательная, указывается с помощью аннотации:
@Autowired(required=false)
Итак, класс AnimalRepository с необязательной зависимостью выглядит так:
@Repository public class AnimalRepository { private Logger logger; @Autowired(required = false) public void setLogger(Logger logger) { this.logger = logger; } public void save() { if (logger != null) logger.log("logged"); System.out.println("save method"); } }
AnimalRepository создается вне зависимости от настройки logger.
А с необязательной зависимостью обходятся так, как показано выше — проверяют ее на null. (Хотя есть еще вариант просто присвоить некий логгер по умолчанию, но опустим этот вариант).
Проверка
Можно написать тест и убедиться, что контекст успешно создается при любом значении свойства logger. А логирование происходит только при logger=true.
@SpringBootTest class MainTests { @Autowired private AnimalRepository animalRepository; @Test void whenLogger_thenLog() { animalRepository.save(); } }
Код примера доступен на GitHub.