Java 获取 spring bean 的新实例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29495353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get new instance of a spring bean
提问by Mr T.
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.
我有一个名为MyInterface
. 实现MyInterface
(让我们调用它MyImplClass
)的类也实现了Runnable
接口,所以我可以用它来实例化线程。这是我现在的代码。
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:
我想要做的是在我的 ApplicationContext.xml 中声明实现类并为每次迭代获取一个新实例。所以我的代码看起来像这样:
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
如果可能的话,我想仍然保留 IoC 模式。
我怎么能这样做?
谢谢
采纳答案by Himanshu Ahire
You can try factory pattern with spring scope prototype like below. Define a Abstract Factory Class which will give you MyInterface
object
您可以尝试使用 spring 范围原型的工厂模式,如下所示。定义一个抽象工厂类,它会给你MyInterface
对象
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).
然后定义 Spring bean.xml 文件如下。请注意myinterface
bean 被定义为原型(所以它总是会给你新的实例)。
<bean name="myinterface" class="com.xxx.MyInterfaceImpl" scope="prototype"/>
Then define the factorybean with factory method name.
然后使用工厂方法名称定义工厂bean。
<bean name="myinterfaceFactory" class="com.xxx.MyInterfaceFactoryImpl">
<lookup-method bean="myinterface" name="getMyInterface" />
</bean>
Now you can call myinterfaceFactory
to get new instance.
现在您可以调用myinterfaceFactory
以获取新实例。
for (OtherClass obj : someList) {
MyInterface myInter = myInterfaceFactory.getMyInterface();
Thread t = new Thread(myInter);
t.start();
}
回答by Ben Kern
If you can determine at runtime which MyImplClass
instance to use, you could list all implementations as beans in your context xml and @Autowire
an array of type MyInterface
to get all MyInterface
implementors.
如果您可以在运行时确定MyImplClass
要使用哪个实例,则可以在上下文 xml 和@Autowire
类型数组中将所有实现列为 beanMyInterface
以获取所有MyInterface
实现者。
Given the following in the context xml:
鉴于上下文 xml 中的以下内容:
<bean class="MyImplClass" p:somethingCaseSpecific="case1"/>
<bean class="MyImplClass" p:somethingCaseSpecific="case2"/>
Then a deceleration
然后减速
@Autowire
MyInterface[] allInterfaceBeans;
will result in allInterfaceBeans
containing both beans defined above.
将导致allInterfaceBeans
包含上面定义的两个 bean。
If you wanted the logic for determining which implementation to use to be done at injection time, you could always @Autowire
a setter method setAllInterfaceBeans(MyInterface[] allInterfaceBeans);
.
如果您想要在注入时确定要使用的实现的逻辑,您总是可以@Autowire
使用 setter 方法setAllInterfaceBeans(MyInterface[] allInterfaceBeans);
。
回答by Albin
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.
将 spring 配置文件 beans.xml 保留在类路径的根目录中。使 scope=prototype,将导致每次 getBean 方法调用产生不同的 bean 实例。
beans.xml
bean.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.
以类似的方式,如果您希望 Spring 在每次需要时返回相同的 bean 实例,您应该将 bean 的 scope 属性声明为单例。
Once the IoC container is initialized, you can retrieve your Spring beans. But make sure, you do the below only initialization only once.
一旦 IoC 容器被初始化,您就可以检索您的 Spring bean。但请确保,您只执行以下初始化一次。
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();
}
回答by MCHAppy
First and foremost, we all know that by default spring container will create bean in singleton mode (if you don't explicitly specify the scope). As the name implies, singleton guarantee that everytime you call the bean, it will give you the same instance. Nevertheless, there's slightly differences between singleton in spring with the one singleton that mentioned by GoF. In Spring, the created instance will be restricted to the container (not JVM as we found in GoF).
首先,我们都知道默认情况下spring容器会以单例模式创建bean(如果你没有明确指定范围)。顾名思义,单例保证每次调用 bean 时,它都会给你相同的实例。尽管如此,spring 中的单例与 GoF 提到的单例之间略有不同。在 Spring 中,创建的实例将被限制在容器中(而不是我们在 GoF 中发现的 JVM)。
Additionally, in spring, you can define two different bean instances of the same type but with different names and they will be two different instances created on the heap. But every time you reference one of those beans by name (ref= in a bean definition or getBean on the appContext), you get the same object every time. That is obviously different than the actual singleton pattern but similar in concept anyway.
此外,在 spring 中,您可以定义两个相同类型但名称不同的不同 bean 实例,它们将是在堆上创建的两个不同实例。但是,每次按名称(bean 定义中的 ref= 或 appContext 中的 getBean)引用这些 bean 时,每次都会获得相同的对象。这显然与实际的单例模式不同,但无论如何在概念上是相似的。
Generally speaking, there are implications of using a singleton in a multi-threaded application (Spring singleton or actual singleton). Any state that you keep on these objects must account for the fact that multiple threads will access it. Usually, any state that exists will be set during instantiation via a setter or constructor argument. This category of Spring bean makes sense for long lived objects, thread-safe objects. If you want something thread specific and still desire spring to create the object, then prototype scope works.
一般来说,在多线程应用程序(Spring 单例或实际单例)中使用单例是有影响的。您在这些对象上保留的任何状态都必须说明多个线程将访问它的事实。通常,任何存在的状态都将在实例化期间通过 setter 或构造函数参数进行设置。这类 Spring bean 对长寿命对象、线程安全对象很有意义。如果你想要一些特定于线程的东西并且仍然希望 spring 创建对象,那么原型范围就可以工作。
回答by nicholas.hauschild
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.
鉴于您在评论中提供给我的上下文,我建议您不要MyImplClass
使用 Spring 创建的实例。由 Spring 实例化这个原型对象并没有从我所知道的中提供任何好处。
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:
在我看来,在这里保持 IoC 模式的最好方法是使用 Spring 管理的 Factory 来生成MyImplClass
. 类似的东西:
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
.
根据使用需求,可以修改这个工厂的接口返回MyImplClass
,或者添加一些逻辑来返回不同的实现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.
我倾向于认为工厂和 IoC/DI 可以很好地协同工作,您的用例就是一个很好的例子。
回答by Gaetan
Initial Note 1
初始注 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:
我建议不要手动创建和启动线程,而是建议使用外部配置的线程池,以便您可以管理创建的线程数。如果大小someList
为 1000,创建这么多线程是低效的。您最好使用由线程池支持的执行程序。Spring 提供了一些可以用作配置了task
命名空间的spring bean 的实现,如下所示:
<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.
queue-capacity
是线程池的最大大小。如果超过该大小,当前线程将运行附加任务,从而阻塞循环,直到另一个线程被释放 ( rejection-policy="CALLER_RUNS"
)。请参阅task:executor
文档,或ThreadPoolExecutor
使用您自己的配置定义任何(spring 或 jdk-concurrent)。
Initial Note 2
初始注 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:
如果您打算存储的唯一状态MyClassImpl
是列表中的项目,那么您可以忘记下面的其余说明(ThreadPool 内容除外),直接使用单例 bean:删除Runnable
接口及其无参数run()
方法,添加一个run(OtherClass obj)
方法并执行如下操作:
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()
.
如果您计划在执行期间run()
(除了已处理的对象)在 MyClassImpl 中存储一些状态,请继续阅读。但是您仍将使用该run(OtherClass obj)
方法而不是 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.
基本思想是根据定义为 spring bean 的某种模型或原型为每个正在运行的线程获取不同的对象。为了实现这一点,只需将最初想要传递给每个线程的 bean 定义为代理,该代理将分派到绑定到正在运行的线程的实例。这意味着相同的任务实例被注入到每个线程中,并且在线程执行期间,您调用方法的真实任务绑定到当前线程。
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) :
我们假设它Program
是一个 spring bean,因此可以注入依赖项。如果Program
不是spring bean,则需要从某处获取spring ApplicationContext,然后自动装配Program
(即根据注解注入在ApplicationContext 中找到的依赖项)。像这样(在构造函数中):
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.
taskTarget
是您定义业务的地方。这个 bean 被定义为一个原型,因为一个新的实例将被分配给每个线程。多亏了这一点,您甚至可以存储取决于run()
参数的状态。这个 bean 永远不会被应用程序直接使用(因此autowire-candidate="false"
),而是通过task
bean使用它。在executeConcurrently()
上面,该行task.run(obj)
实际上将taskTarget
在代理创建的原型之一上分派。