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

В предыдущем посте использование @Autowired хорошо работает и обеспечивает желаемую модульность, но определение того, где именно объявляются определения autowired bean, все еще остается несколько неоднозначным. Например, как разработчик, просматривающий ServiceConfig, как узнать, где именно объявлен компонент @Autowired AccountRepository? Это не явно указано в коде, и это может быть нормально. Помните, что Spring Tools для Eclipse предоставляет инструменты, которые могут отображать графики, показывающие, как все подключено, что может быть тем, что вам нужно. Кроме того, ваша Java IDE может легко найти все объявления и варианты использования типа AccountRepository и быстро показать вам расположение методов @Bean, возвращающих этот тип.

В случаях, когда эта двусмысленность неприемлема и вы хотите иметь прямую навигацию из вашей IDE от одного класса @Configuration к другому, рассмотрите возможность автоматического подключения самих классов конфигурации. В следующем примере показано, как это сделать:

Java

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // переходим через класс конфигурации к @Bean методу 
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

Kotlin

@Configuration
class ServiceConfig {

    @Autowired
    private lateinit var repositoryConfig: RepositoryConfig

    @Bean
    fun transferService(): TransferService {
        // переходим через класс конфигурации к @Bean методу
        return TransferServiceImpl(repositoryConfig.accountRepository())
    }
}

В предыдущей ситуации AccountRepository определен полностью явно. Однако теперь ServiceConfig тесно связан с RepositoryConfig. Это компромисс. Эту тесную связь можно несколько смягчить, используя классы @Configuration на основе интерфейса или абстрактные классы. Рассмотрим следующий пример:

Java

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

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

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

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

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return 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
    private lateinit var repositoryConfig: RepositoryConfig

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

@Configuration
interface RepositoryConfig {

    @Bean
    fun accountRepository(): AccountRepository
}

@Configuration
class DefaultRepositoryConfig : RepositoryConfig {

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

@Configuration
@Import(ServiceConfig::class, DefaultRepositoryConfig::class)  // import the concrete config!
class SystemTestConfig {

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

}

fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}

Теперь ServiceConfig слабо связан с конкретным DefaultRepositoryConfig, и встроенные инструменты IDE по-прежнему полезны: вы можете легко получить иерархию типов реализаций RepositoryConfig. Таким образом, навигация по классам @Configuration и их зависимостям ничем не отличается от обычного процесса навигации по коду на основе интерфейса.

Если вы хотите повлиять на порядок создания определенных bean-компонентов при запуске, рассмотрите возможность объявления некоторых из них как @Lazy (для создания при первом доступе, а не при запуске) или как @DependsOn для некоторых других bean-компонентов (убедитесь, что другие специфичные bean-компоненты созданы до текущего bean, помимо тех, что подразумевают прямые зависимости последнего).


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


Комментарии

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

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

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

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