Spring bean с аргументами конструктора времени исполнения

Я хочу создать bean-компонент Spring в Spring Java с некоторыми аргументами конструктора, передаваемыми во время выполнения. Я создал следующую конфигурацию 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 не может подключиться, потому что бин не найден. Как я могу создать компонент с аргументами конструктора во время выполнения?

Я использую Spring 4.2

Ответ 1

Вы можете использовать прототип 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 не будет создавать экземпляр компонента прямо при запуске, но сделает это позже по требованию. Теперь, чтобы настроить экземпляр компонента-прототипа, вы должны сделать следующее.

@Controller
public class ExampleController{

   @Autowired
   private BeanFactory beanFactory;

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

Обратите внимание: поскольку ваш бин не может быть создан при запуске, вы не должны напрямую связывать свой бин; в противном случае Spring попытается создать экземпляр самого компонента. Это использование приведет к ошибке.

@Controller
public class ExampleController{

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

}

Ответ 2

Вы правильно выглядите, чтобы получить прототип с параметрами, используйте метод BeanFactory # getBean (String name, Object... args).

Посмотрите Spring Конфигурация Java: как вы создаете прототип @Bean с аргументами времени выполнения? BeanFactory # getBean (String name, Object... args) было бы тем, что вы ищете.

Я полагаю, что ваш IDEA (в моем случае IntelliJ IDEA версии 15.) дает вам ошибку и не является ошибкой времени выполнения/компиляции.

В IntelliJ вы можете изменить настройки проверок Spring.

  • Перейдите в файл → настройки.
  • Введите проверки в поле поиска.
  • Перейдите в Spring Core- > Code- > Autowire для классов Bean.
  • Изменить с "Ошибка" на "слабое предупреждение"

Ответ 3

Решение выглядит отлично, но у меня есть вопрос о прототипе, если мы снова вызовем контроллер и передадим то же значение beanFactory.getBean(TdctFixedLengthReport.class, "sourceSystem");

Spring создаст новый объект или использует предыдущий?

Ответ 4

Это может быть достигнуто с ObjectProvider<> класса Spring ObjectProvider<> который был представлен в Spring 4.3. См. Spring документацию для получения дополнительной информации.

Суть в том, чтобы определить метод фабрики ObjectProvider<> для предоставляемого объекта, внедрить 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 делает это для вас автоматически. Вам просто нужно определить метод фабрики бобов.