@Conditional бин и необязательная зависимость @Autowired(required=false)

В общем случае рекомендуется внедрять зависимости через конструктор. Но существуют необязательные зависимости, которые невозможно внедрить через конструктор. В этой статье как раз и пойдет речь о такой зависимости, она внедряется через setter.

@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. Иначе он не создается.

Соответственно мы не можем внедрять его в другой бин AnimalRepository через конструктор AnimalRepository. Потому что внедрение через конструктор подразумевает, что все внедряемые бины уже созданы к моменту вызова конструктора. То есть сначала Logger, потом AnimalRepository. В случае же logger=false бин Logger отсутствует, и поэтому будет ошибка создания бина  AnimalRepository.

Поэтому необязательный бин внедряется через сеттер.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *