Spring IoC контейнер: Java конфигурация, композиция конфигураций на основе Java, внедрение зависимостей от импортированных определений @Bean

Пример в предыдущем посте работает, но он упрощен. В большинстве практических сценариев bean-компоненты зависят друг от друга в разных классах конфигурации. При использовании XML это не проблема, потому что компилятор не задействован, и вы можете объявить ref="someBean" и доверять Spring, чтобы решить эту проблему во время инициализации контейнера. При использовании классов @Configuration компилятор Java накладывает ограничения на модель конфигурации, так как ссылки на другие bean-компоненты должны иметь допустимый синтаксис Java.

К счастью, решить эту проблему просто. Метод @Bean может иметь произвольное количество параметров, описывающих зависимости bean-компонентов. Рассмотрим следующий более реальный сценарий с несколькими классами @Configuration, каждый из которых зависит от bean-компонентов, объявленных в других:

Java

@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // все подключается к классам конфигурации...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

Kotlin

import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

    @Bean
    fun transferService(accountRepository: AccountRepository): TransferService {
        return TransferServiceImpl(accountRepository)
    }
}

@Configuration
class RepositoryConfig {

    @Bean
    fun accountRepository(dataSource: DataSource): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

    @Bean
    fun dataSource(): DataSource {
        // return new DataSource
    }
}


fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    // все подключается к классам конфигурации...
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}

Есть другой способ добиться такого же результата. Помните, что классы @Configuration - это, в конечном счете, всего лишь еще один компонент в контейнере: это означает, что они могут использовать преимущества внедрения @Autowired и @Value, а также другие функции, как и любой другой компонент.

Убедитесь, что зависимости, которые вы вводите таким образом, являются только простейшими. Классы @Configuration обрабатываются довольно рано во время инициализации контекста, и принудительное внедрение зависимости таким образом может привести к неожиданной ранней инициализации. По возможности используйте инъекцию на основе параметров, как в предыдущем примере.

Также будьте особенно осторожны с определениями BeanPostProcessor и BeanFactoryPostProcessor через @Bean. Обычно их следует объявлять как статические методы @Bean, не запускающие создание экземпляра содержащего их класса конфигурации. В противном случае @Autowired и @Value могут не работать с самим классом конфигурации, поскольку его можно создать как экземпляр компонента раньше, чем AutowiredAnnotationBeanPostProcessor.

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

Java

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // все подключается к классам конфигурации...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

Kotlin

import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

    @Autowired
    lateinit var accountRepository: AccountRepository

    @Bean
    fun transferService(): TransferService {
        return TransferServiceImpl(accountRepository)
    }
}

@Configuration
class RepositoryConfig(private val dataSource: DataSource) {

    @Bean
    fun accountRepository(): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

    @Bean
    fun dataSource(): DataSource {
        // return new DataSource
    }
}

fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    // все подключается к классам конфигурации...
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}

Внедрение конструктора в классы @Configuration поддерживается только в Spring Framework 4.3. Также обратите внимание, что нет необходимости указывать @Autowired, если целевой bean-компонент определяет только один конструктор.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки