Get new instance of a spring bean

44,892

Solution 1

You can try factory pattern with spring scope prototype like below. Define a Abstract Factory Class which will give you MyInterface object

public abstract class MyInterfaceFactoryImpl implements MyInterfaceFactory {

@Override
public abstract MyInterface getMyInterface();

}

Then define the Spring bean.xml file as below. Please note myinterface bean is defined as prototype ( So it will always give you new instance).

<bean name="myinterface" class="com.xxx.MyInterfaceImpl" scope="prototype"/>

Then define the factorybean with factory method name.

<bean name="myinterfaceFactory" class="com.xxx.MyInterfaceFactoryImpl">
    <lookup-method bean="myinterface" name="getMyInterface" />
</bean>

Now you can call myinterfaceFactory to get new instance.

for (OtherClass obj : someList) {
        MyInterface myInter = myInterfaceFactory.getMyInterface();
        Thread t = new Thread(myInter);
        t.start();
}

Solution 2

Initial Note 1

Instead of creating and starting threads by hand, I would suggest to use a pool of threads that is externally configured, so that you can manage the number of threads that are created. If the size of someList is 1000, creating so many threads is inefficient. You should better use an executor backed by a pool of threads. Spring provides some implementations that can be used as spring beans configured with the task namespace, something like this:

<task:executor id="executor" queue-capacity="10" rejection-policy="CALLER_RUNS" />

queue-capacity is the max size of the threads pool. If that size is exceeded, the current thread will run the additional task, thus blocking the loop until another thread is freed (rejection-policy="CALLER_RUNS"). See the task:executor documentation, or define any ThreadPoolExecutor (spring or jdk-concurrent) with your own configuration.

Initial Note 2

If the only state that you intend to store in MyClassImpl is the item from the list, then you can forget the rest of the explanation below (except for the ThreadPool stuff), and directly use a singleton bean : remove the Runnable interface and its no-arg run() method, add a run(OtherClass obj) method and do something like this:

final MyInterface task = // get it from spring as a singleton
for (final OtherClass obj : someList) {
  executor.execute(new Runnable() {
    public void run() {task.run(obj);}
  });
  // jdk 8 : executor.execute(task::run);
}

If you plan to store some state inside MyClassImpl during the execution of run() (other than the processed object), go on reading. But you will still use the run(OtherClass obj) method instead of no-args run().

The basic idea is to get a different object for each running thread, based on some kind of model or prototype defined as a spring bean. In order to achieve this, just define the bean that you initially want to pass to each thread as a proxy that dispatches to an instance that is bound to the running thread. This means that the same instance of task is injected into each thread, and during the thread execution, the real task on which you invoke methods is bound to the current thread.

Main program

Since you are using the elements of the list to do your business, you will pass each element to its owning task.

public class Program {
  @Resource private MyInterface task; // this is a proxy
  @Resource private TaskExecutor executor;

  public void executeConcurrently(List<OtherClass> someList) {
    for (final OtherClass obj : someList) {
      executor.execute(new Runnable() {
        public void run() { task.run(obj); }
      });
      // jdk 8 : executor.execute(task::run);
    }
  }
}

We suppose that Program is a spring bean, thus the dependencies can be injected. If Program is not a spring bean, you will need to get the spring ApplicationContext from somewhere, then autowire Program (i.e. inject dependencies found in the ApplicationContext, based on annotations). Something like this (in the constructor) :

public Program(ApplicationContext ctx) {
  ctx.getAutowireCapableBeanFactory().autowireBean(this);
}

Define the task

<bean id="taskTarget" class="MyImplClass" scope="prototype" autowire-candidate="false" />

<bean id="task" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="targetSource">
    <bean class="org.springframework.aop.target.ThreadLocalTargetSource">
      <property name="targetBeanName" value="taskTarget"/>
      <property name="targetClass" value="MyInterface"/>
    </bean>
  </property>
</bean>

taskTarget is where you define your business. This bean is defined as a prototype, as a new instance will be allocated to each thread. Thanks to this, you can even store state that depends on the run() parameter. This bean is never used directly by the application (thus autowire-candidate="false"), but it is used through the task bean. In executeConcurrently() above, the line task.run(obj) will actually be dispatched on one of the prototype taskTarget that was created by the proxy.

Solution 3

Keep the spring configuration file, beans.xml in the root of the classpath. Making scope=prototype, will result in different instances of bean for each getBean method invocation.

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="myinterface" class="MyImplClass" scope="prototype"/>
</beans>

Similar way if you want Spring to return the same bean instance each time one is needed, you should declare the bean's scope attribute to be singleton.

Once the IoC container is initialized, you can retrieve your Spring beans. But make sure, you do the below only initialization only once.

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Then you can change your code as below.

for (OtherClass obj : someList) {
MyInterface myInter = (MyInterface ) context.getBean("myinterface");
Thread t = new Thread(myInter);
t.start();
}

Solution 4

Given the context you provided in your comment to me, I would suggest you don't have the MyImplClass instances created by Spring. Having this prototyped object instantiated by Spring provides no benefit from what I can tell.

The best way, in my opinion, to keep with the IoC pattern here would be to instead utilize a Spring managed Factory that produces instances of MyImplClass. Something along the lines of this:

public class MyInterfaceFactory {
    public MyInterface newInstance(final OtherClass o) {
        return new MyImplClass(o);
    }
}

Depending on the usage needs, you can modify this factory's interface to return MyImplClass, or add some logic to return a different implementation of MyInterface.

I tend to think that Factories and IoC/DI work pretty well together, and your use case is a pretty good example of that.

Share:
44,892
Mr T.
Author by

Mr T.

Updated on April 09, 2020

Comments

  • Mr T.
    Mr T. about 4 years

    I have an interface called MyInterface. The class that implements MyInterface (lets call it MyImplClass) also implements the Runnable interface so i can use it to instantiate threads. This is my code now.

    for (OtherClass obj : someList) {
        MyInterface myInter = new MyImplClass(obj);
        Thread t = new Thread(myInter);
        t.start();
    } 
    

    What i want to do is to declare the implementing class in my ApplicationContext.xml and get a new instance for each iteration. So my code will look something like this:

    for (OtherClass obj : someList) {
        MyInterface myInter = // getting the implementation from elsewhere
        Thread t = new Thread(myInter);
        t.start();
    } 
    

    I want to still keep the IoC pattern if possible.
    How can i do so?
    Thanks