【问题标题】:How to use Pooled Spring beans instead of Singleton ones?如何使用池化的 Spring bean 而不是 Singleton 的?
【发布时间】:2011-06-27 13:29:50
【问题描述】:

出于效率原因,我有兴趣限制同时使用 Spring 应用程序上下文的 bean 的线程数(我不希望 无限 在我的 中处理线程数有限的内存)。

我发现here(spring 文档)通过以下方式以 EJB 样式汇集 bean 来实现此目的:

  • 将目标 bean 声明为范围“原型”。
  • 声明将提供有限数量的池化“目标”实例的池提供程序。
  • 声明一个“ProxyFactoryBean”,我不清楚它的功能。

这是这个 bean 的声明:

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" 
    scope="prototype">
  ... properties omitted
</bean>

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
  <property name="targetBeanName" value="businessObjectTarget"/>
  <property name="maxSize" value="25"/>
</bean>

<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="targetSource" ref="poolTargetSource"/>
  <property name="interceptorNames" value="myInterceptor"/>
</bean>

我的问题是,当我将声明另一个 bean 使用“businessObjectTarget”的池化实例时,我应该怎么做?我的意思是,当我尝试做这样的事情时:

<bean id="clientBean" class="com.mycompany.ClientOfTheBusinessObject">
  <property name="businessObject" ref="WHAT TO PUT HERE???"/>
</bean>

“ref”的值应该是多少??

【问题讨论】:

  • 如果您的意思是“businessObjectTarget”,那么我不会使用任何池,而是使用“MyBusinessObject”的单个实例。如果您的意思是“businessObject”,则没有任何使用该名称声明的 bean。
  • ?
  • @abalogh 对不起,我不明白,你的 cmets 不是很冗长。您建议声明“ProxyFactoryBean”类型的 bean businessObject?这已经在代码中完成了。

标签: java multithreading spring ejb


【解决方案1】:

您不能使用属性来获取原型的实例。
一种选择是使用查找方法(请参阅chapter 3.3.7.1
在代码中获取 bean 的另一种选择:使您的 com.mycompany.ClientOfTheBusinessObject 实现 ApplicationContextAware 接口,然后调用 context.getBean("clientBean")

【讨论】:

  • 你的意思是说,如果我从应用程序上下文中以编程方式检索它们,我只能使用池 bean,但我不能使用“注入池依赖”??
  • 任何具有原型作用域的bean(不仅是池bean)都不能以“正常方式”注入,因为“正常方式”只会出现一次。我建议您阅读我的回答中的第 3.3 章,它解释得很好。
  • 谢谢。我知道,如果您以“正常方式”注入原型,它只会发生一次。在池化的情况下,我对 Spring 的期望是,它通过代理设法调用池化实例,如果没有可用的实例,则阻塞线程。从技术上讲,它拦截其他调用以管理事务性等的方式是可能的……但似乎 Spring 不支持此功能(EJB 支持)。
  • @edutesoy,我认为 ProxyFactoryBean 与 CommonsPoolTargetSource 的结合完全符合您的意图。来自文档:“使用池化目标源提供了与无状态会话 EJB 类似的编程模型,其中维护了一个相同实例的池,方法调用将释放池中的对象。”
【解决方案2】:

请注意spring示例中第三个bean的名称:-"businessObject"

这意味着这是您应该访问公共池的 bean。

对于您的情况,如果您需要自己的客户端 bean,您可以使用如下方式。 但在这种情况下,不需要 businessObject。:-

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" 
    scope="prototype">

  ... properties omitted
</bean>

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
  <property name="targetBeanName" value="businessObjectTarget"/>
  <property name="maxSize" value="25"/>
</bean>

<bean id="clientBean" class="com.mycompany.ClientOfTheBusinessObject">
 <property name="poolTargetSource" ref="poolTargetSource"/>
</bean>


Java classes:-
public class ClientOfTheBusinessObject{
     CommonsPoolTargetSource poolTargetSource;
//<getter and setter for poolTargeTSource>
  public void methodToAccessCommonPool(){
     //The following line gets the object from the pool.If there is nothing left in the pool then the thread will be blocked.(The blocking can be replaced with an exception by changing the properties of the CommonsPoolTargetSource bean)
     MyBusinessObject mbo = (MyBusinessObject)poolTargetSource.getTarget();
     //Do whatever you want to do with mbo
     //the following line puts the object back to the pool
     poolTargetSource.releaseTarget(mbo);
  }
}

【讨论】:

    【解决方案3】:

    我很确定您可以以一种不那么复杂的方式限制同时线程的数量。您是否查看过 Java 并发 API,特别是 Executors.newFixedThreadPool() ?

    【讨论】:

      【解决方案4】:

      我使用 java-configuration 在处理池的接口上构造了一个代理,使用 apache commons-pool 来实现调用级池。

      【讨论】:

        【解决方案5】:

        我是使用基于注释的配置完成的:

        1. 我确实将 BusinessObject 类创建为 POJO 并以这种方式对其进行注释:

          @Component("businessObject")
          @Scope("prototype")
          public class BusinessObject { ... }
          

          我给了它一个具体的名字,并把它标记为prototype,这样Spring就不会为它创建一个单例实例;每次需要 bean 时,Spring 都会创建一个新实例。

        2. 在我的@Configuration 类中(或者在@SpringBootApplication 类中,如果使用Spring Boot)我创建了一个CommonsPool2TargetSource 实例来保存BusinessObject 实例:

          @Bean
          public CommonsPool2TargetSource pooledTargetSource() {
              final CommonsPool2TargetSource commonsPoolTargetSource = new CommonsPool2TargetSource();
              commonsPoolTargetSource.setTargetBeanName("businessObject");
              commonsPoolTargetSource.setTargetClass(BusinessObject.class);
              commonsPoolTargetSource.setMaxSize(maxPoolSize);
              return commonsPoolTargetSource;
          }
          

          这里我表示池将保存BusinessObject 实例。请注意,我的maxPoolSize=? 值设置为我想在池中保存的BusinessObject 实例的最大数量。

        3. 最后,我确实以这种方式访问​​了我的池实例:

          @Autowired
          private CommonsPool2TargetSource pooledTargetSource;
          
          void someMethod() {
              // First I retrieve one pooled BusinessObject instance
              BusinessObject businessObject = (BusinessObject)pooledTargetSource.getTarget();
          
              try {
                  // Second, I do some logic using the BusinessObject instance gotten
              } catch (SomePossibleException e) {
                  // Catch and handle any potential error, if any
              } finally {
          
                  // Finally, after executing my business logic
                  // I release the BusinessObject instance so that it can be reused
                  pooledTargetSource.releaseTarget(businessObject);
              }
          }
          

          始终确保从池中释放BusinessObject 借来的非常重要,无论业务逻辑是成功完成还是出错。否则,池可能会因为所有实例都被借用而永远不会释放,并且对实例的任何进一步请求将永远阻塞。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-06
          • 1970-01-01
          • 2023-03-23
          • 1970-01-01
          相关资源
          最近更新 更多