В общем случае рекомендуется внедрять зависимости через конструктор. Но необязательные зависимости рекомендуется внедрять через сеттер.
В этой статье пойдет речь о @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.