如何使用带有构造函数参数的 Spring Prototype Beans?
How to use Spring Prototype Beans with constructor arguments?
我确实使用 Spring 和 Lombok。
如果没有原型 bean,我们必须传递目标 class 需要的依赖项。
我们如何将 bean 标记为原型并正确处理依赖 bean 和构造函数参数?
选项 1 - 没有原型 beans
@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 - 原型 bean
@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;
}
}
我的评论:
选项 1:
简洁明了。并非每个 class 都需要声明为 Spring bean。如果 class 足够简单,不使用任何 Spring 特征(例如 @Cache
、 @Tranascational
等),那么可以 KISS 并且不将其声明为Spring bean 并手动创建它。 Consumer
就像 Main class 一样来驱动逻辑,所以我想说 Consumer
在某种意义上也需要 SomeDependency
,因为它需要它们来驱动逻辑创建 Processor
.
选项 2:
同意你的看法。不太好,因为我们需要单独的调用和额外的 "initialized" 属性 来确保 Processor 与选项 1 相比正确创建,我们只需要通过构造函数创建它。但是 Processor
是一个 Spring bean,因此我们可以很容易地对其应用 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)
on Processor
@Bean 是必需的。否则,Spring 将在启动期间尝试查找具有 String 类型的 bean 来创建处理器。由于没有String类型的bean,所以会抛出异常。将 autowireCandidate
设置为 false 可以禁用 Spring 创建它。毕竟,我们将从 ProcessorFactory
手动创建它
我确实使用 Spring 和 Lombok。
如果没有原型 bean,我们必须传递目标 class 需要的依赖项。
我们如何将 bean 标记为原型并正确处理依赖 bean 和构造函数参数?
选项 1 - 没有原型 beans
@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 - 原型 bean
@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;
}
}
我的评论:
选项 1:
简洁明了。并非每个 class 都需要声明为 Spring bean。如果 class 足够简单,不使用任何 Spring 特征(例如 @Cache
、 @Tranascational
等),那么可以 KISS 并且不将其声明为Spring bean 并手动创建它。 Consumer
就像 Main class 一样来驱动逻辑,所以我想说 Consumer
在某种意义上也需要 SomeDependency
,因为它需要它们来驱动逻辑创建 Processor
.
选项 2:
同意你的看法。不太好,因为我们需要单独的调用和额外的 "initialized" 属性 来确保 Processor 与选项 1 相比正确创建,我们只需要通过构造函数创建它。但是 Processor
是一个 Spring bean,因此我们可以很容易地对其应用 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)
on Processor
@Bean 是必需的。否则,Spring 将在启动期间尝试查找具有 String 类型的 bean 来创建处理器。由于没有String类型的bean,所以会抛出异常。将 autowireCandidate
设置为 false 可以禁用 Spring 创建它。毕竟,我们将从 ProcessorFactory