在 Spring 中允许并发的正确 Bean 的 proxyMode 是啥?

Posted

技术标签:

【中文标题】在 Spring 中允许并发的正确 Bean 的 proxyMode 是啥?【英文标题】:What is the correct Bean's proxyMode for allowing concurrency in Spring?在 Spring 中允许并发的正确 Bean 的 proxyMode 是什么? 【发布时间】:2018-10-01 14:43:27 【问题描述】:

我正在构建一个基于 Spring Framework 的库,我希望允许用户并行调用库的方法。

在我的主类中,我自动装配服务类:

@Autowired
private ExportListCommand exportList;

这就是图书馆方法的实现:

public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) 
    exportList.setoAuthClient(oAuthClient);
    ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);

    return result;

ExportListCommand 被定义为一个 Bean:

@Bean
@Scope("prototype")
public ExportListCommand exportList() 
    return new ExportListCommand();

当我作为库用户并行运行 2 个 exportList 的方法时,Spring 只创建一个 ExportListCommand bean,因为它只自动装配一次。但实际上我需要 2 个独立的 ExportListCommand bean。我还尝试将@Scope(value="prototype") 更改为@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS),但这也无法按我的需要工作:Spring 为每个方法调用创建ExportListCommand bean,并且由于我得到新对象而丢失了oAuthClient 值。

我让它只适用于我想避免的 AnnotationConfigApplicationContext.getBean() 方法。

我的选择是什么?谢谢。

【问题讨论】:

【参考方案1】:

我相信您正在寻找使用“工厂”对象。

从 Spring 的角度来看,我有两种主要的考虑方式。

    “Java”方式:创建一个工厂对象,该对象将返回ExportListCommand 的实例

这个工厂看起来像这样:

class ExportListCommandFactory 
    ExportListCommand newInstance() 
        return new ExportListCommand();
    

并且会像这样在你的方法中使用:

@Autowire
private ExportListCommandFactory commandFactory;

public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) 
    final ExportListCommand exportList = commandFactory.newInstance();
    exportList.setoAuthClient(oAuthClient);
    ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);

    return result;

当然,这样做需要您更改配置以包含 ExportListCommandFactory 而不是 ExportListCommand 的 bean。

或者,您可以考虑...

    “弹簧”方式:使用FactoryBean

这里你唯一需要做的是,在你的主类中,@Autowire 一个FactoryBean&lt;ExportListCommand&gt; 而不是ExportListCommand,并且在你需要调用方法的方法中,咨询工厂以获得你的实例。

@Autowire
private FactoryBean<ExportListCommand> commandFactory;

public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) 
    final ExportListCommand exportList = commandFactory.getObject();
    exportList.setoAuthClient(oAuthClient);
    ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);

    return result;

您不需要更改配置,因为 FactoryBean 是一个特殊的 bean,它会在每次调用 getObject() 时为实例查询 ApplicationContext/BeanFactory。

【讨论】:

非常感谢您精心制作的答案。由于FactoryBean 是一个接口,我应该实现它,对吗?在实现内部,我有几乎相同的选择:调用 new ExportListCommand() 这将破坏所有 IoC 依赖或再次调用 AnnotationConfigApplicationContext。我没有提到,但我有十几个命令,我不想为每个命令单独的工厂,所以工厂应该知道如何返回适当的命令,我想我可以用泛型来实现。此外,Command 依赖于相关的 Response bean,因此 IoC 应该执行繁重的工作 你应该不需要实现工厂bean。 Spring 识别该类并对其进行特殊处理。按照我写的去做,它应该可以工作。 这很奇怪。 Spring 给了我:“*** 中的字段 commandFactory 需要一个找不到的 'org.springframework.beans.factory.FactoryBean' 类型的 bean。行动:考虑定义一个 'org.springframework.beans.factory 类型的 bean。 FactoryBean' 在您的配置中。”你知道这个接口的默认实现类是什么吗?

以上是关于在 Spring 中允许并发的正确 Bean 的 proxyMode 是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring 安全性中允许除一个之外的所有 URL

Spring各种注解标签作用详解

仅在 Spring Security 5 中允许分号用于某些特定的 url,即 StrictHttpFirewall?

spring bean id和bean name的区别

在 MVC 正则表达式中允许一些但不是所有的空格

如何在 JetBrains Rider 中允许不安全的代码?