Я использую 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. То, как сформулирован вопрос, вызывает мнения, но на него вполне можно ответить объективно. Это хороший вопрос, который нуждается в настройке.
Вы можете найти ответ в Spring bean с аргументами конструктора времени выполнения.
Спасибо за ваш отзыв, и я переформулировал свой вопрос.




Мои комментарии:
Опция 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. Это просто стоит вам дополнительного класса.
Это рабочий пример? Пожалуйста, обновите пустоту в фабричном классе. Кроме того, как вы можете читать String requestData в фабричном классе? У меня выдает ошибку "Could not Autowire".
хороший улов . давайте попробуем использовать @Bean(autowireCandidate = false) при определении bean-компонента-прототипа.
Привет @ken, я использовал autowireCandidate = false, но все еще получаю сообщение об ошибке «требуется bean-компонент типа 'java.lang.String', который не может быть найден». ты знаешь почему?
Здравствуйте @AdiV, вы нашли решение этой проблемы?
Я предполагаю, что вы получаете отрицательные голоса, потому что вопрос о «меньшем зле» полностью субъективен. Возможно, перефразируйте вопрос с точки зрения неизменности и всего, чего вы хотите достичь.