Spring 具有运行时构造函数参数的 bean

Spring bean with runtime constructor arguments

我想在 Spring Java 配置 中创建一个 Spring bean,并在运行时传递一些构造函数参数。我创建了以下 Java 配置,其中有一个 bean fixedLengthReport 需要构造函数中的一些参数。

@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

您的代码看起来不错,要使用 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)给了你错误,它不是 runtime/compile 时间错误。

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

  • 转到文件 -> 设置。
  • 在搜索框中键入检查。
  • 转到 Spring Core->Code->Bean 的自动装配 类。
  • 从"Error"改为“弱警告”

您可以将原型 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不能在启动时实例化,你不能直接自动装配你的bean;否则 Spring 将尝试实例化 bean 本身。这种用法会导致错误。

@Controller
public class ExampleController{

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

}

这可以通过在 Spring 4.3 中引入的 Spring 的 ObjectProvider<> class 来实现。有关详细信息,请参阅 Spring 的 documentation

要点是为要提供的对象定义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工厂方法。