Внедрение зависимостей Spring через @Configuration, @Bean и @Component - PullRequest
2 голосов
/ 04 июля 2019

Я изо всех сил пытался понять, как работает внедрение зависимостей с помощью аннотаций @Configuration, @Bean и @Component.

Мой код выглядит следующим образом.

1) Route.kt

/* Route.kt */

package com.example.service

import com.example.service.ports.AutoComplete
import com.example.service.ports.Validation
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.web.reactive.function.server.router

@Configuration
@Suppress("unused")
class Routes(private val autoComplete: AutoComplete,
             private val validation: Validation) {

    @Bean
    fun route() = router {
        ("/service-lookup" and accept(APPLICATION_JSON)).nest {
            GET("/auto-complete/{service}", autoComplete::autoComplete)
            GET("/validation/{service}", validation::validation)
        }
    }
}

2) ServiceImpl.kt

package com.example.service

import com.example.service.ports.AutoComplete
import com.example.service.ports.Validation
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

@Component
@Suppress("unused")
class ServiceImpl: AutoComplete, Validation {
    override fun autoComplete(request: ServerRequest): Mono<ServerResponse> {
        TODO("not implemented autoComplete") //To change body of created functions use File | Settings | File Templates.
    }

    override fun validation(request: ServerRequest): Mono<ServerResponse> {
        TODO("not implemented validation") //To change body of created functions use File | Settings | File Templates.
    }
}

3) ports/AutoComplete.kt

package com.example.service.ports

import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

interface AutoComplete {

    fun autoComplete(request: ServerRequest): Mono<ServerResponse>

}

4) ports/Validation.kt

package com.example.service.ports

import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

interface Validation {

    fun validation(request: ServerRequest): Mono<ServerResponse>

}

У меня вопрос: откуда бин route, созданный из Route.kt, знает, что autoComplete и validation должны использовать класс ServiceImpl из ServiceImpl.kt?

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Попробую уточнить:

@ Component - Это жестко связано с автоматической настройкой пружины и сканированием компонента.Все, помеченное Компонентом, будет выбрано, если оно у вас есть на пути сканирования компонента, определенного аннотацией @ComponentScanned.@Components бывают трех видов:

A) Хранилища - используются для постоянства

B) Контроллеры, например RestController

C) Служба - служба без состояния.F.ex a Facade.

Эта аннотация используется для автоматизации построения контекста приложения, а также для определения стереотипа для bean-компонента, привязанного к контексту.

@Bean - @Bean и @Component имеют одну и ту же цель, но @Bean не является @Component.Они оба создают контекст приложения, но делают это совсем по-другому.В то время как @Component определяет стереотип класса и говорит Spring забрать его.Бин несет полную ответственность за ручную настройку экземпляра того, что вы создаете.Реализация и конфигурация полностью разделены, и вы получаете более высокую степень контроля над тем, как именно генерируется Бин.

@ Configuration используется вместе с @Bean.@Bean в отличие от @Component является аннотацией уровня метода, поэтому обычно случается, что класс помечается @Configuration, а затем следуют еще один или несколько методов, аннотированных @Bean.

В вашем конкретном случаепример Вы создали маршрутизатор @Bean.Маршрутизатор был создан на основе автозаполнения и проверки, введенных в маршруты.Spring смог выяснить, что вводить, основываясь на наиболее подходящем кандидате.Поскольку у вас есть один экземпляр компонента, реализующий два интерфейса AutoComplete, он проверяет его.В вашем случае автозаполнение и проверка будут указывать на один и тот же экземпляр.

1 голос
/ 04 июля 2019

Следующее описание механизма Spring упрощено для вашего примера:

При запуске Spring Boot сканирует путь к классам для всех классов, помеченных @Configuration, @Component и т. Д., И создает список определений бина. В вашем примере он находит классы Routes и ServiceImpl.

После этого Spring сканирует все методы каждого класса в списке определения компонента для дальнейших аннотаций @Bean и добавляет метод (особенно тип возвращаемого значения) в список определения компонента. В вашем примере он находит метод route.

После первого сканирования Spring знает, какие типы компонентов присутствуют и какой класс компонентов реализует, какие интерфейсы. И Spring знает, какие параметры конструктора, Inject цели или параметры метода необходимы для создания экземпляра компонента. С этой информацией Spring создает экземпляры бинов в правильном порядке. В вашем примере Spring знает, что Router требует AutoComplete и Validation и что ServiceImpl реализует эти интерфейсы. Поэтому он должен сначала создать ServiceImpl, а затем Router.

Если более одного компонента реализует один и тот же интерфейс, Spring выдает исключение, и вам необходимо дополнительно определить компонент.

...