如何在SpEL中按类型引用bean?

问题描述 投票:0回答:2

以下是一个最小的示例,显示了我的问题。

Main.kt

package com.mycompany.configurationpropertiestest

import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service


@SpringBootApplication
@EnableScheduling
@EnableConfigurationProperties(FooServiceConfig::class)
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}


@ConstructorBinding
@ConfigurationProperties("configurationpropertiestest.foo")
data class FooServiceConfig(
    val interval: Int = 1000,
    val message: String = "hi"
)


@Service
class FooService(
    private val myConfig: FooServiceConfig
) {
    private val log = LoggerFactory.getLogger(this.javaClass)
    //@Scheduled(fixedDelayString = "#{@FooServiceConfig.interval}")
    //@Scheduled(fixedDelayString = "#{@myConfig.interval}")
    @Scheduled(fixedDelayString = "\${configurationpropertiestest.foo.interval}")
    fun talk() {
        log.info(myConfig.message)
    }
}

([@ConstructorBinding用于允许FooServiceConfig的成员不变。]

application.yml

configurationpropertiestest:
  foo:
    interval: 500
    message: "hi"

Test.kt

package com.mycompany.configurationpropertiestest

import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner


@RunWith(SpringRunner::class)
@SpringBootTest
class Test {
    @Test
    fun `sit and wait`() {
        Thread.sleep(3000)
    }
}

它可以工作,但只能这样做,因为我在interval批注中像这样引用@Scheduled

@Scheduled(fixedDelayString = "\${configurationpropertiestest.foo.interval}")

这在某种程度上破坏了我的服务的隔离配置。它突然必须了解外部事物,现在应该知道这些。

理想情况下,它只能通过bean的类型来访问其配置:

@Scheduled(fixedDelayString = "#{@FooServiceConfig.interval}")

或通过注入的实例:

@Scheduled(fixedDelayString = "#{@myConfig.interval}")

但是这些尝试分别导致No bean named 'FooServiceConfig' availableNo bean named 'myConfig' available

关于如何实现只能访问config bean而不访问全局config值的任何想法?

spring spring-boot kotlin spring-annotations spring-el
2个回答
0
投票

我更改了一些代码,对我来说它可以正常工作。主要变化是用FooServiceConfig注入@Autowired。然后在调度程序中,我可以这样写:"#{@fooServiceConfig.interval}"

@SpringBootApplication
@EnableScheduling
class Application

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties("configurationpropertiestest.foo")
data class FooServiceConfig(
        var interval: Int = 1000,
        var message: String = "hi"
)

@Service
class FooService {
    private val log = LoggerFactory.getLogger(this.javaClass)

    @Autowired
    lateinit var fooServiceConfig:FooServiceConfig

    @Scheduled(fixedDelayString = "#{@fooServiceConfig.interval}")
    fun talk() {
        log.info(fooServiceConfig.message)
    }
}

0
投票

如果您不介意将FooService.myConfig设为公开,这应该可以:

@Service
class FooService(val myConfig: FooServiceConfig) {

    val log = LoggerFactory.getLogger(this.javaClass)

    @Scheduled(fixedDelayString = "#{@fooService.myConfig.interval}")
    fun talk() {
        log.info(myConfig.message)
    }
}

UPDATE:

[显然,Spring将用@ConstructorBinding注释注释的bean的名称调整为[configuration-properties-value]-[fully-qualified-bean-name]FooServiceConfig最终以configurationpropertiestest.foo-com.mycompany.configurationpropertiestest.FooServiceConfig

因此,尽管很丑陋,但这也应该起作用:

@Service
class FooService(private val myConfig: FooServiceConfig) {

    val log = LoggerFactory.getLogger(this.javaClass)

    @Scheduled(fixedDelayString = "#{@'configurationpropertiestest.foo-com.mycompany.configurationpropertiestest.FooServiceConfig'.interval}")
    fun talk() {
        log.info(myConfig.message)
    }
}

最后,最后一个选项,回答标题问题:如何在SpEL中按类型引用bean?您可以通过调用beanFactory.getBean

来完成此操作
@Service
class FooService(private val myConfig: FooServiceConfig) {

    val log = LoggerFactory.getLogger(this.javaClass)

    @Scheduled(fixedDelayString = "#{beanFactory.getBean(T(com.mycompany.configurationpropertiestest.FooServiceConfig)).interval}")
    fun talk() {
        log.info(myConfig.message)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.