试图在 Spring Boot 中理解 kotlin DI

Posted

技术标签:

【中文标题】试图在 Spring Boot 中理解 kotlin DI【英文标题】:Trying to understand kotlin DI in Spring boot, at all 【发布时间】:2019-10-03 15:26:54 【问题描述】:

由于某种原因,我在 Spring Boot 中掌握 kotlin 风格的 DI 仍然存在巨大问题。尤其是在 JUnit 测试中。

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest(classes = [(ApplicationConfig::class)])
class DataServiceTest 

private var dataService: DataService
private var addressRepository: AddressRepository

@Test
fun shouldUpdateCustomerEmail() 

    dataService.setNewCustomerEmail("1212", ApiEmail("test@test.org"))

  

我有非常简单的测试。 我尝试了几种注射方式。

private var dataService: DataService

错误:必须初始化属性(有意义)

private lateinit var dataService: DataService

错误:lateinit 属性 dataService 尚未初始化(嗯,是的,我还没有初始化它)

@Autowired
private lateinit var dataService: DataService

错误:找不到“DataService”类型的 bean(为什么?在其他服务中没问题)

class DataServiceTest @Autowired constructor(
        private val dataService: DataService
)  ... 

错误:找不到“DataService”类型的 bean(为什么?在其他服务中没问题)

private var dataService: DataService = DataService()

没有错误。

但不能使用相同的样式为地址注入接口存储库,因为没有接口的构造函数, 所以存储库将未初始化并再次在setNewCustomerEmail()中抛出错误

DataService 是具有单一方法的 @Service 类(目前),在从 rest 调用时工作正常,但从 @Test 调用时崩溃

@Service
class DataService  

    @Autowired
    private lateinit var addressRepository: AddressRepository

    fun setNewCustomerEmail(id: String, email: ApiEmail) 
        val addressList = addressRepository.findAddressById( id.toInt() )
        ...
    
 

我确实明白为什么会出现大多数这些错误,但是似乎有很多方法可以在 kotlin + spring boot 中 DI 其他依赖项(类和变量),但是对于接下来的每个用例来说似乎都不同,这很好,但我似乎无法掌握何时使用什么。

我如何掌握 kotlin DI?

如何正确注入这个存储库接口?

到目前为止我已经阅读了:

How to use spring annotations like @Autowired in kotlin?

https://www.bignerdranch.com/blog/kotlin-when-to-use-lazy-or-lateinit/

编辑 此外

https://spring.io/guides/tutorials/spring-boot-kotlin/

显示以下代码:

interface UserRepository : CrudRepository<User, Long> 
  fun findByLogin(login: String): User

然后:

@DataJpaTest
class RepositoriesTests @Autowired constructor(
    val entityManager: TestEntityManager,
    val userRepository: UserRepository,
    val articleRepository: ArticleRepository)  ... 

这几乎正是我在这里尝试的......但是如果我尝试使用 DataService 执行 @Autowire 构造函数,我会得到错误:再次找不到“DataService”类型的bean。

class DataServiceTest @Autowired constructor(
        val dataService: DataService
) ... no beans of type 'DataService' found

如果我尝试在 DataService 中使用 AddressRepository 作为构造函数参数,我现在必须传入一个 Repository,或者使用 @Bean,我没有?

class DataService @Autowired constructor(
        private val AddressRepository: AddressRepository
)

我对此感到非常困惑......

【问题讨论】:

【参考方案1】:

我建议使用构造函数注入来为您的服务定义所有依赖项:

@Service
class DataService(private val addressRepository: AddressRepository) 
   fun setNewCustomerEmail() ...

请注意,您可以在此处省略@Autowired。

要测试您可以使用普通单元测试并自行设置对象:

class DataServiceTest 
   val mock    = mock<AddressRepository>() // e.g. Mockito
   val service = DataService(mock)

   @Test
   fun myTest() 
     ...
   

您也可以使用 SpringRunner 编写集成测试。然后 Spring 将处理您的服务和存储库的创建和注入。

@RunWith(SpringRunner::class)
@SpringBootTest
class DataServiceTest 
   @Autowired
   lateinit var dataService: DataService

由于您似乎使用 JUnit4 AFAIK,因此无法使用构造函数注入。因此,您需要使用 @Autowired/lateinit 组合。使用 JUnit5 时,应该可以在测试类中定义一个额外的构造函数。

【讨论】:

以上是关于试图在 Spring Boot 中理解 kotlin DI的主要内容,如果未能解决你的问题,请参考以下文章

无法理解 Spring Boot 如何使用 maven 管理集成测试。它是不是使用故障安全插件?

Spring Boot jpa 无法插入包含重音的字符串的行

Spring Boot 中的 PersistenceContext 生命周期

Spring Boot2 系列教程理解Spring Boot 配置文件 application.properties

Vaadin 10没有在Spring-Boot中使用模板

Spring Boot 连接池理解