«1. Обзор

Spring по умолчанию управляет жизненным циклом бинов и устанавливает порядок их инициализации.

Но мы все еще можем настроить его в соответствии с нашими потребностями. Мы можем выбрать либо интерфейс SmartLifeCycle, либо аннотацию @DependsOn для управления порядком инициализации.

В этом руководстве рассматривается аннотация @DependsOn и ее поведение в случае отсутствия bean-компонента или циклической зависимости. Или в случае просто необходимости инициализации одного компонента перед другим.

2. Maven

Прежде всего, давайте импортируем зависимость spring-context в наш файл pom.xml. Мы всегда должны обращаться к Maven Central за последней версией зависимостей:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

3. @DependsOn

Мы должны использовать эту аннотацию для указания зависимостей компонентов. Spring гарантирует, что определенные bean-компоненты будут инициализированы перед попыткой инициализации текущего bean-компонента.

Допустим, у нас есть FileProcessor, который зависит от FileReader и FileWriter. В этом случае FileReader и FileWriter должны быть инициализированы до FileProcessor.

4. Конфигурация

Файл конфигурации представляет собой чистый класс Java с аннотацией @Configuration:

@Configuration
@ComponentScan("com.baeldung.dependson")
public class Config {
 
    @Bean
    @DependsOn({"fileReader","fileWriter"})
    public FileProcessor fileProcessor(){
        return new FileProcessor();
    }
    
    @Bean("fileReader")
    public FileReader fileReader() {
        return new FileReader();
    }
    
    @Bean("fileWriter")
    public FileWriter fileWriter() {
        return new FileWriter();
    }   
}

FileProcessor указывает свои зависимости с помощью @DependsOn. Мы также можем аннотировать компонент с помощью @DependsOn:

@Component
@DependsOn({"filereader", "fileWriter"})
public class FileProcessor {}

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

Давайте создадим один файл класса. Каждый из bean-компонентов обновляет текст в файле File. FileReader обновляет его как прочитанный. FileWriter обновляет его по мере записи, а FileProcessor обновляет текст по мере обработки:

@Test
public void WhenFileProcessorIsCreated_FileTextContains_Processed() {
    FileProcessor processor = context.getBean(FileProcessor.class);
    assertTrue(processor.process().endsWith("processed"));
}

5.1. Отсутствует зависимость

В случае отсутствия зависимости Spring генерирует исключение BeanCreationException с базовым исключением NoSuchBeanDefinitionException. Подробнее об исключении NoSuchBeanDefinitionException читайте здесь.

Например, bean-компонент dummyFileProcessor зависит от bean-компонента dummyFileWriter. Так как dummyFileWriter не существует, он генерирует исключение BeanCreationException:

@Test(expected=NoSuchBeanDefinitionException.class)
public void whenDependentBeanNotAvailable_ThrowsNosuchBeanDefinitionException(){
    context.getBean("dummyFileProcessor");
}

5.2. Циклическая зависимость

Кроме того, в этом случае генерируется BeanCreationException и подчеркивается, что компоненты имеют циклическую зависимость:

@Bean("dummyFileProcessorCircular")
@DependsOn({"dummyFileReaderCircular"})
@Lazy
public FileProcessor dummyFileProcessorCircular() {
    return new FileProcessor(file);
}

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

Bean1 -> Bean4 -> Bean6 -> Bean1

6. Ключевые моменты

Наконец, есть несколько моментов, о которых мы должны позаботиться при использовании аннотации @DependsOn:

    При использовании @DependsOn мы должны использовать компонентное сканирование. объявленный через XML, метаданные аннотации DependsOn игнорируются

7. Заключение

@DependsOn становится особенно полезным при построении систем со сложными требованиями к зависимостям.

Это облегчает внедрение зависимостей, гарантируя, что Spring выполнит всю инициализацию этих необходимых компонентов перед загрузкой нашего зависимого класса.

Как всегда, код можно найти на GitHub.