Mr.Eddart
Mr.Eddart

Reputation: 10273

How to use Pooled Spring beans instead of Singleton ones?

For efficiency reasons, I am interested in limiting the number of threads that simultaneously uses the beans of the Spring application context (I don't want an unlimited number of threads proccessing in my limited memory).

I have found here (spring documentation) a way to achieve this by pooling the beans in a EJB style, by doing the following:

Here is the declaration of this beans:

<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>

My problem is that when I will declare another bean to use pooled instances of the "businessObjectTarget", how should I do it? I mean, when i try to do something like this:

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

What should be the value of the "ref" ??

Upvotes: 3

Views: 9911

Answers (5)

Francisco Acevedo
Francisco Acevedo

Reputation: 420

I did it using Annotations based configuration:

  1. I did create my BusinessObject class as a POJO and annotate it this way:

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

    I gave it a specific name and did mark it as prototype so that Spring doesn't create a singleton instance for it; every time the bean is required, Spring would create a new instance.

  2. In my @Configuration class (or in the @SpringBootApplication class, if using Spring Boot) I created a CommonsPool2TargetSource instance to hold BusinessObject instances:

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

    Here I'm indicating that the pool will hold BusinessObject instances. Notice that my maxPoolSize=? value is set with the max number of BusinessObject instances I want to hold in the pool.

  3. Finally, I did access my pooled instances this way:

    @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);
        }
    }
    

    It is very important to always make sure to release the BusinessObject borrowed from the pool, without mattering if the business logic did finish successfully or with error. Otherwise the pool could get empty with all the instances being borrowed and never released and any further requests for instances will block forever.

Upvotes: 0

Jijo Mathew
Jijo Mathew

Reputation: 352

Please note the name of the third bean in the spring example:-"businessObject"

It means this the bean from where you are supposed to access the common pool.

For your case if you need your own client bean you may have it as follows. But in such a case businessObject is not required.:-

<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);
  }
}

Upvotes: 1

cproinger
cproinger

Reputation: 2288

i used java-configuration to construct a proxy over the interface that handles pooling using apache commons-pool to achieve invocation-level-pooling.

Upvotes: 0

Olaf
Olaf

Reputation: 6289

I'm pretty sure you can limit the number of simultaneous threads in a less convoluted way. Did you look at the Java Concurrency API, specifically at the Executors.newFixedThreadPool() ?

Upvotes: 0

Tarlog
Tarlog

Reputation: 10154

You cannot use properties to get instances of prototypes.
One option is to use the lookup methods (see chapter 3.3.7.1)
Another option to get your bean in code: make your com.mycompany.ClientOfTheBusinessObject to implement the ApplicationContextAware interface and then call context.getBean("clientBean")

Upvotes: 5

Related Questions