Как использовать Spring Prototype Beans с аргументами конструктора?

Я использую Spring и Lombok.
. Без bean-компонентов-прототипов нам приходится обходить зависимости, которые требуются целевому классу.
Как пометить bean-компонент как прототип и правильно обработать зависимые bean-компоненты и аргументы конструктора?

Опция 1 — Нет прототипов bean-компонентов

@Component @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Consumer {
  private final SomeDependency iDontNeed; // Consumer class doesn't need
  private final SomeDependency2 iDontNeed2;

  public void method() {
    new Processor("some random per request data", iDontNeed, iDontNeed2);
  }
....
@Value @RequiredArgsConstructor
public class Processor {
  private final String perRequestInputData;
  private final SomeDependency iReallyNeed;
  private final SomeDependency2 iReallyNeed2;
}

Вариант 2 — бины-прототипы

@Component @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Consumer {
  private final Provider<Processor> processorProvider;

  public void method() {
    Processor p = processorProvider.get();
    p.initializeWith("some random per request data");
  }
....
@Component @Scope("prototype") 
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Processor {
  private final SomeDependency iReallyNeed;
  private final SomeDependency2 iReallyNeed2;

  private String perRequestInputData; //wish I was final
  private boolean initialized; //wish I was not needed

  public void initializeWith(String perRequestInputData) {
    Preconditions.checkState(!initialized);
    this.perRequestInputData = perRequestInputData
    initialized = true;
  }
}

Я предполагаю, что вы получаете отрицательные голоса, потому что вопрос о «меньшем зле» полностью субъективен. Возможно, перефразируйте вопрос с точки зрения неизменности и всего, чего вы хотите достичь.

jaco0646 26.01.2019 00:59

Я согласен с @ jaco0646. То, как сформулирован вопрос, вызывает мнения, но на него вполне можно ответить объективно. Это хороший вопрос, который нуждается в настройке.

Don Branson 26.01.2019 01:14

Вы можете найти ответ в Spring bean с аргументами конструктора времени выполнения.

jaco0646 26.01.2019 01:49

Спасибо за ваш отзыв, и я переформулировал свой вопрос.

Ram 30.01.2019 01:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
4
3 518
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мои комментарии:

Опция 1:

Это чисто и просто. Не каждый класс должен быть объявлен как bean-компонент Spring. Если класс достаточно прост и не использует какие-либо функции Spring (такие как @Cache, @Tranascational и т. д.), можно использовать KISS, а не объявлять его как компонент Spring и создавать вручную. Consumer, как и основной класс, управляет логикой, поэтому я бы сказал, что SomeDependency также требуется Consumer в некотором смысле, поскольку он нуждается в них для управления логикой создания Processor.

Вариант 2:

Согласен с тобой. Не так приятно, поскольку нам нужен отдельный вызов и дополнительное свойство «инициализировано», чтобы убедиться, что процессор создан правильно по сравнению с вариантом 1, который нам просто нужно создать с помощью конструктора. Но Processor — это компонент Spring, поэтому мы можем очень легко применить к нему функцию Spring.

Есть ли у нас альтернативы?

Моя альтернатива — использовать заводской шаблон с @Configuration и @Bean, чтобы получить лучшее из обоих миров:

Сначала определите фабрику:

@Configuration
public class ProcessorFactory {

     @Autowired
     private final SomeDependency dep1; 

     @Autowired
     private final SomeDependency2 dep2;

     @Bean(autowireCandidate = false)
     @Scope("prototype") 
     public Processor createProcessor(String requestData) {
        return new Processor(requestData, dep1, dep2);
    }
}

Затем в потребителе:

@Component
public class Consumer {

     @Autowired 
     private final ProcessorFactory processorFactory;


      public void method() {
        Processor p = processorFactory.createProcessor("some random per request data");
        p.blablbaba();
      }
}

Примечание: необходим @Bean(autowireCandidate = false) на Processor @Bean. В противном случае Spring попытается найти bean-компонент с типом String для создания процессора во время запуска. Поскольку bean-компонента с типом String не существует, будет выдано исключение. Установка для autowireCandidate значения false может отключить Spring для его создания. Ведь мы создадим его вручную из ProcessorFactory

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

jaco0646 26.01.2019 18:51

Это рабочий пример? Пожалуйста, обновите пустоту в фабричном классе. Кроме того, как вы можете читать String requestData в фабричном классе? У меня выдает ошибку "Could not Autowire".

Black Diamond 17.10.2019 22:34

хороший улов . давайте попробуем использовать @Bean(autowireCandidate = false) при определении bean-компонента-прототипа.

Ken Chan 18.10.2019 21:21

Привет @ken, я использовал autowireCandidate = false, но все еще получаю сообщение об ошибке «требуется bean-компонент типа 'java.lang.String', который не может быть найден». ты знаешь почему?

Hussain 21.04.2021 09:24

Здравствуйте @AdiV, вы нашли решение этой проблемы?

Hussain 21.04.2021 09:36

Другие вопросы по теме