Spring Bean 在运行时传递构造函数参数

43

我希望在Spring Java配置中创建一个Spring bean,并在运行时传递一些构造函数参数。 我已经创建了以下Java配置,在其中有一个bean fixedLengthReport,该bean期望在构造函数中传递一些参数。

@Configuration
public class AppConfig {

    @Autowrire
    Dao dao;

    @Bean
    @Scope(value = "prototype")
    **//SourceSystem can change at runtime**
    public FixedLengthReport fixedLengthReport(String sourceSystem) {
         return new TdctFixedLengthReport(sourceSystem, dao);
    }
}

但是我遇到了错误,提示sourceSystem无法连接,因为没有找到bean。如何在运行时使用构造函数参数创建bean?

我正在使用Spring 4.2。


你在哪里定义了 SourceSystem 的 bean? - user2004685
SourceSystem不是一个Spring Bean。我们可以说它只是一个字符串,在运行时确定其值。我已经更新了我的问题。 - suraj bahl
你能提供 TdctFixedLengthReport 的实现吗? - user2004685
你在用什么IDEA? - Haim Raman
@surajbahl:这个错误可能是因为Spring不知道TdctFixedLengthReport的实例。在这里,TdctFixedLengthReport的实例是由程序创建而不是由Spring创建的。使用@Autowired注解,这样TdctFixedLengthReport的实例就会由Spring自己创建。 - Eves
3个回答

75

您可以使用原型bean和BeanFactory一起使用。

@Configuration
public class AppConfig {

   @Autowired
   Dao dao;

   @Bean
   @Scope(value = "prototype")
   public FixedLengthReport fixedLengthReport(String sourceSystem) {
       return new TdctFixedLengthReport(sourceSystem, dao);
   }
}

@Scope(value = "prototype") 的意思是Spring不会在启动时立即实例化该bean,而是在需要时稍后实例化。 现在,要自定义原型bean的实例,您需要执行以下操作。

@Controller
public class ExampleController{

   @Autowired
   private BeanFactory beanFactory;

   @RequestMapping("/")
   public String exampleMethod(){
      TdctFixedLengthReport report = 
         beanFactory.getBean(TdctFixedLengthReport.class, "sourceSystem");
   }
}

请注意,因为您的bean无法在启动时实例化,所以您不能直接进行Autowire;否则Spring将尝试实例化该bean。这种使用方式会导致错误。

@Controller
public class ExampleController{

   //next declaration will cause ERROR
   @Autowired
   private TdctFixedLengthReport report;

}

3
请更正@Autowire 注解中的拼写错误。(是的,这个错误让我非常困扰!;)) - Dominik
2
@Dominik 非常感谢。下次如果你发现有同样的错别字,请随意编辑我的帖子。) - Ken Bekov
似乎编辑至少要编辑6个字符,至少在我尝试编辑问题(1个字符)和您的帖子(3个字符编辑)时是这样的 :-) - Dominik
@KenBekov 这已经是原型范围了,将在调用时启动,为什么我们需要懒加载呢?谢谢 - Spring
2
@Spring 是正确的。Prototype 将在调用时被初始化。因此,对于 Prototype bean,不需要使用 Lazy。这只是为了演示概念,但实际上这里的 Lazy 是一种冗余。 - Ken Bekov
显示剩余3条评论

12
这可以通过Spring 4.3中引入的ObjectProvider<>类来实现。请参阅Spring的文档了解更多细节。
要点是定义要提供的对象的bean工厂方法,将ObjectProvider<>注入到您的使用者中,并创建要提供的对象的新实例。
public class Pair
{
    private String left;
    private String right;

    public Pair(String left, String right)
    {
        this.left = left;
        this.right = right;
    }

    public String getLeft()
    {
        return left;
    }

    public String getRight()
    {
        return right;
    }
}

@Configuration
public class MyConfig
{
    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public Pair pair(String left, String right)
    {
        return new Pair(left, right);
    }
}

@Component
public class MyConsumer
{
    private ObjectProvider<Pair> pairProvider;

    @Autowired
    public MyConsumer(ObjectProvider<Pair> pairProvider)
    {
        this.pairProvider = pairProvider;
    }

    public void doSomethingWithPairs()
    {
        Pair pairOne = pairProvider.getObject("a", "b");
        Pair pairTwo = pairProvider.getObject("z", "x");
    }
}

注意:你实际上不需要实现ObjectProvider<>接口;Spring会自动为你完成这一部分。你只需要定义bean工厂方法即可。


5

你的代码看起来不错。如果要使用带有参数的原型,请使用BeanFactory#getBean(String name,Object ... args)方法。

参考Spring Java Config: how do you create a prototype-scoped @Bean with runtime arguments?,BeanFactory#getBean(String name,Object ... args)是您要寻找的内容。

我猜你的IDEA(在我的情况下是IntelliJ IDEA版本15.)给出的错误并不是运行时/编译时错误。

在IntelliJ中,您可以更改Spring检查的设置。

  • 转到文件 -> 设置。
  • 在搜索框中键入“inspections”。
  • 进入Spring Core->Code->Autowire for Bean Classes。
  • 将“Error”更改为“weak warning”

没错,我正在使用15版本的IDEA,可能是因为这个原因。我会更改设置以删除它。谢谢你的帮助,Haim。 - suraj bahl
这个错误困扰了我一段时间。谢谢你指出如何关闭它的方法! - B.Z.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接