Cleaner way to get new instance of an autowired fi

2020-02-26 13:12发布

I faced this issue, while trying to autowire a runnable class and creating different instances of it in different call and keeping it in an array.

xml configuration is :

<bean name="threadName" Class="ABC" scope="prototype" />

In my code, I am trying something like this:

public class ThreadHandler{


@Autowired
private ABC threadName;

//getter
ABC getThreadName(){
     return threadName;
}

public void someFunction(){
     List<ABC> abc = new ArrayList(ABC>();
     for (int i=0;i<SOME_CONST;i++){
          ABC tName = getThreadName();
          abc.add(tName);
          tName.start();
      }
}   

}

Let ABC be a class which is Thread/Runnable/Callable.

In this way, it throws java.lang.IllegalThreadStateException. But, it works fine, if I use ABC tName =appContext.getBean("threadName",ABC.class);

Why does it happen?

Don't we get a new instance while trying to get an object from getMethod?

2条回答
再贱就再见
2楼-- · 2020-02-26 13:47

No, you don't get a new object, just by accessing a field that holds a reference to a bean scoped as Prototype. Spring doesn't have any way to replace your instance level reference with a new reference based just on field access. It's set once by the autowiring and then it's just a reference to that one object. If you want it to actually create a new one you need to use getBean as you observed.

You can tell Spring to override your get method and replace it with a 'getBean' using method injection, to get the Spring dependencies out of your bean:

<bean id="threadHandler" class="com.stackexchange.ThreadHandler">
<lookup-method name="getThreadName" bean="threadName"/>
</bean>
查看更多
聊天终结者
3楼-- · 2020-02-26 13:48

There is much better practice when you need to create Runnable/Callable and inject it into applicationContext it's called look up method:

Let's consider that all Runnable/Callable classes are @Prototype and @Lazy

@Component(value="task")
@Scope(value="prototype")
@Lazy(value=true)
public class Task implements Runnable {

public void run(){
.....
}

}

Now you need to Create Look up method factory:

    <bean id="taskFactory" class="x.y.z.TaskFactory">
<lookup-method name="createTask" bean="task"/>
</bean>

Now let's implement TaskFactory itself which is abstract class and have one abstract method :

@Component(value="taskFactory")
public abstract class TaskFactory {

    public abstract Task createTask();

}

Here comes the magic:

public class ThreadHandler{

@Autowired
private TaskFactory taskFactory;


public void someFunction(){
          Runnable task = taskFactory.createTask();
          taskExecutor.execute(task);
      }
}   

Every time you are calling createTask() method of taskFactory singleton object. you will receive completely new instance of your prototype object.

P.S: don't forget to add

<context:annotation-config />
    <context:component-scan base-package="x.y.z"></context:component-scan>

to enable Annotations correctly.

hope it Helps.

查看更多
登录 后发表回答