You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala Object中使用Spring Beans?注入异常与配置问题求解

问题描述

Scala代码示例

@Component
class A (@Autowired val componentB: B) {Something}

@Component
class B (@Autowired val beanC: C) {Something}

@Component
class D () { 
  @Value("${configProperty}")
  val configProperty = ""

  @Bean
  def foobar: C = {Something}
}

@SpringBootApplication
class MainClass extends ApplicationRunner {
  def main(): Unit = {
    SpringApplication.run(classOf[MainClass])
  }
  override def run(): Unit = {Do nothing}
}

@EnableAutoConfiguration
@Component
object SparkRunnable extends MainClass {
  override def run(): Unit = {
    val annotationContext = new AnnotationConfigApplicationContext(classOf[SparkRunnable])
    val wiredClass = annotationContext.getBean(classOf[A])
  }
}

遇到的错误

初始运行错误

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'A': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'B' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

手动装配后的错误

尝试手动装配代码:

val uselessWiringVal = annotationContext.getBean(classOf[D])
val uselessWiringObj = new B(uselessWiringVal.foobar)
val wiredClass = new A(uselessWiringObj)

出现错误:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'A' defined in URL[SparkRunnable]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'D': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'configProperty' in value "${configProperty}"

核心疑问

  1. 组件类的配置是否存在问题?
  2. 如何在SparkRunnable这个object类型的类中获取可用的Spring组件?

注:因Spark运行要求,SparkRunnable必须是object类型。尝试过object SparkRunnable (@Autowired A) extends MainClass {}这类语法但无效;可在SparkRunnable对应的类中实现Autowired注入,但无法传递给伴生对象。


解决方案

1. 组件类配置问题分析

你的组件类定义本身没问题,错误根源是你在SparkRunnable的run方法里新建了一个独立的AnnotationConfigApplicationContext——这个新容器和SpringBoot启动的主容器完全隔离,主容器中初始化的Bean在新容器里根本不存在,所以会出现找不到B的错误。

手动装配时读不到配置,也是因为新容器没有加载SpringBoot的配置环境,无法解析${configProperty}这类占位符。

2. 在SparkRunnable Object中获取组件的正确方式

Scala的object是语言层面的单例,Spring无法直接为它注入依赖,可通过以下两种方式解决:

方式一:复用Spring主容器上下文

在SpringBoot启动时,把主容器上下文保存到全局可访问的地方,SparkRunnable直接从这里获取Bean:

@SpringBootApplication
class MainClass extends ApplicationRunner {
  override def run(args: ApplicationArguments): Unit = {
    // 启动Spring并保存上下文到全局持有类
    val context = SpringApplication.run(classOf[MainClass], args.getSourceArgs: _*)
    SpringContextHolder.setContext(context)
  }
}

// 全局Spring上下文持有类,需要被Spring扫描到
@Component
object SpringContextHolder extends ApplicationContextAware {
  private var context: ApplicationContext = _

  override def setApplicationContext(applicationContext: ApplicationContext): Unit = {
    context = applicationContext
  }

  def getContext: ApplicationContext = context
}

@EnableAutoConfiguration
@Component
object SparkRunnable extends MainClass {
  override def run(): Unit = {
    // 直接从全局上下文获取Bean
    val wiredClass = SpringContextHolder.getContext.getBean(classOf[A])
    // 后续业务逻辑
  }
}

方式二:将业务逻辑转移到Spring管理的组件中

把需要使用Spring组件的逻辑放到一个被Spring管理的类里,SparkRunnable仅作为入口,通过全局上下文调用这个组件:

// 被Spring管理的业务类,可直接注入依赖
@Component
class SparkBusinessService @Autowired()(val a: A) {
  def execute(): Unit = {
    // 直接使用注入的A组件
    println(a.componentB.beanC)
  }
}

@EnableAutoConfiguration
@Component
object SparkRunnable extends MainClass {
  override def run(): Unit = {
    val service = SpringContextHolder.getContext.getBean(classOf[SparkBusinessService])
    service.execute()
  }
}

关键注意事项

  • 绝对不要在SparkRunnable里创建新的Spring容器,必须复用SpringBoot启动的主容器。
  • Scala的object无法被Spring直接注入,只能通过全局上下文间接获取Bean。
  • SpringContextHolder要放在Spring扫描的包下(比如主包或子包),确保Spring能识别并设置上下文。

内容的提问来源于stack exchange,提问作者Blu2718

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:51:20