【问题标题】:Spring injects a bean other than what is specified in @ConfigurationSpring 注入的 bean 不是 @Configuration 中指定的
【发布时间】:2021-07-25 21:14:55
【问题描述】:

最近我在 Spring 中连接 bean 时出错,导致我无法复制的行为。部署应用程序时,使用了在 @Configuration 中定义的另一个 String 类型的 bean 的值,而不是将源自 @Value 的属性注入到 Stuff(参见下面的完整演示代码)中。

我觉得令人费解的是,在本地运行时(包括单元测试),一切都按预期工作,输出是 foo 而不是 kaboom,而且这种“bean 交换”在部署时发生,而不是“否”合格 bean' 错误。

注释掉的行显示了我认为使配置类似于the manual 中的配置的修复。

我的设置有什么问题?什么会使所示代码(即没有修复)使用 kaboom String 而不是 foo 属性?

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
open class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
open class Config {

    // ...beans of types other than String in original code...

    @Bean
    open fun beanBomb(): String {
        return "kaboom"
    }

    @Bean
    // fix:
    // @Value("\${stuff}")
    open fun beanStuff(stuff: String): Stuff {
        return Stuff(stuff)
    }
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component

@Component
class Stuff(@Value("\${stuff}") val stuff: String)
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct

@Component
class Init {

    @Autowired
    private lateinit var stuff: Stuff

    @PostConstruct
    fun init() {
        println("stuff: " + stuff.stuff)
    }
}
// application.properties
stuff=foo
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@TestPropertySource(properties = {"stuff=testFoo"})
class DemoApplicationTests {

    @SpyBean
    private Stuff stuff;

    @Test
    void test() {
        assertEquals("testFoo", stuff.getStuff());
    }

}

另外,应用修复后,Stuff 中的 @Value 注释是否必要?如果我取消注释修复,请从 Stuff 中删除 @Value 并将以下注释添加到测试通过的测试类:

@ContextConfiguration(classes = {Config.class})

但是当我运行应用程序时,它会打印kaboom...

【问题讨论】:

  • 在Stuff类上放置@Component注解并同时在java配置中定义它有什么意义(带有@Bean注解的方法)?

标签: spring spring-boot kotlin properties-file spring-annotations


【解决方案1】:

问题是注释需要在参数而不是函数上进行。 以您的方式,Spring 正在寻找一个满足 String 类型的 bean,并且函数 beanBomb() 生成了一个类型为 String 的 bean。如果您像这样移动注释,它应该消除歧义。

    @Bean
    open fun beanStuff(@Value("\${stuff}") stuff: String): Stuff {
        return Stuff(stuff)
    }

我会补充一点,拥有String 类型的 bean 有点不寻常,但我想如果您不想使用属性/yaml 文件,它允许您更改 String 基于个人资料。

【讨论】:

    【解决方案2】:

    您可以检查创建 bean 的顺序。如果 bean 是在我看来之前创建的,那么 Spring IoC 容器会按类型注入值,即kaboom,并且由于任何类型的 Bean 默认情况下都是单例的,Stuff 的实例不会生效,即使它用@component注解。

    在您的测试中,您正在手动加载配置,其中 Config 中定义的 Stuff 的 bean 被注入,而不是带有 @component 注释的 Stuff。

    【讨论】:

    • 所以原始代码的问题是我在两个地方(@Bean vs @Component)创建了 bean Stuff,一个从 @Value 注释中获取参数,另一个从在Config 中创建的String bean。碰巧当我在本地运行时,Spring 创建了 @Component 注释 Stuff 和 stuff 来自 @Value,而当应用程序部署时 Spring 必须创建 @Bean 注释 Stuff 和 stuff来自beanBomb()。正确的?如果是这样,很遗憾 Spring 没有警告多个 bean 声明,而是从很多中挑选任何一个。
    • 是的,它可能是..您可以添加一个调试器点或添加一些日志消息,只是为了识别订购 bean 的创建和其他选项是使用 @Priority 或 @order注释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 2021-03-01
    • 2020-05-22
    • 1970-01-01
    相关资源
    最近更新 更多