【问题标题】:How to use Spring Prototype Beans with constructor arguments? [closed]如何使用带有构造函数参数的 Spring Prototype Beans? [关闭]
【发布时间】:2019-01-25 22:58:09
【问题描述】:

我确实使用 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 - 原型 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;
  }
}

【问题讨论】:

  • 我假设您收到了反对票,因为关于“较小的邪恶”的问题完全是主观的。也许用不变性和您希望实现的任何其他方面来重新表述这个问题。
  • 我同意@jaco0646。问题的措辞方式会引起意见,但完全可以客观地回答。这是一个需要调整的好问题。
  • 感谢您的反馈,我已经改写了我的问题。

标签: java spring design-patterns lombok


【解决方案1】:

我的cmets:

选项 1:

它干净简单。并非每个类都需要声明为 Spring bean。如果一个类足够简单且不使用任何 Spring 特性(例如 @Cache@Tranascational 等),则可以 KISS 并且不将其声明为 Spring bean 并手动创建它。 Consumer 就像 Main 类来驱动逻辑一样,所以我想说 SomeDependency 在某种意义上也是 Consumer 所必需的,因为它需要它们来驱动创建 Processor 的逻辑。

选项 2:

同意你的看法。不太好,因为我们需要单独的调用和额外的“初始化”属性来确保处理器被正确创建,与我们只需要通过构造函数创建它的选项 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) 上的 Processor @Bean 是必需的。否则,Spring 将在启动期间尝试查找 String 类型的 bean 来创建 Processor。由于没有 String 类型的 bean,它会抛出异常。将 autowireCandidate 设置为 false 可以禁用 Spring 创建它。毕竟,我们将通过ProcessorFactory手动创建它

【讨论】:

  • 请注意,在我对 OP 的评论中,Spring 已经通过其BeanFactory 接口实现了这个工厂模式。滚动您自己的工厂的优点包括围绕运行时参数的类型安全性和消除 Consumer 中的 Spring 依赖项。只是多花一门课而已。
  • 这是一个有效的例子吗?请更新工厂类中的 void。另外,您如何在工厂类中读取 String requestData?它为我显示错误“无法自动装配”。
  • 好收获。让我们在定义原型 bean 时尝试使用@Bean(autowireCandidate = false)
  • 你好@ken,我使用了autowireCandidate = false,但我仍然得到“需要一个找不到的'java.lang.String'类型的bean”错误。你知道为什么吗?
  • 你好@AdiV 你找到解决这个问题的方法了吗?
猜你喜欢
  • 2016-09-20
  • 2020-03-03
  • 1970-01-01
  • 1970-01-01
  • 2016-07-14
  • 2015-02-03
  • 2019-12-19
  • 2019-03-10
  • 1970-01-01
相关资源
最近更新 更多