在Spring中将原型bean注入到单例bean中
问题
当我们将原型bean注入单例bean时,原型bean仍然表现得像单例bean。
让我们看个例子
- 创建一个简单的Java Maven项目。
- Maven依赖性将Spring和Cglib Maven依赖于Pom.xml。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- cglib required for @configuration annotation -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
<properties>
<spring.version>4.2.1.RELEASE</spring.version>
</properties>
所以你的pom.xml看起来像:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"<
<modelVersion<4.0.0</modelVersion>
<groupId>org.igi.theitroad</groupId>
<artifactId>SpringHelloWorldJavaBasedConfiguration</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringHelloWorldJavaBasedConfiguration</name>
<description>It provides hello world program for java based config
</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- cglib required for @configuration annotation -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
<properties>
<spring.version>4.2.1.RELEASE</spring.version>
</properties>
</project>
3.创建bean类
在package org.igi.theitroad中创建一个名为x.java的bean类。
package org.igi.theitroad;
import org.springframework.beans.factory.annotation.Autowired;
public class X {
@Autowired
Y y;
public Y getY() {
return y;
}
public void setY(Y y) {
this.y = y;
}
}
在package org.igi.theitroad中创建一个名为y.java的 Bean类类。
package org.igi.theitroad;
public class Y {
public void methodY()
{
System.out.println("In method Y");
}
}
- ApplicationContext.xml在SRC/Main/Resources中创建ApplicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="x" class="org.igi.theitroad.X">
</bean>
<bean id="y" class="org.igi.theitroad.Y" scope="prototype">
</bean>
<context:annotation-config
</beans>
我们刚刚在此声明了两个bean x和y。
5.创建EnjectProTotyPetosingloronMain.java.
package org.igi.theitroad;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Injecting prototype dependencies into singleton
*
*/
public class InjectPrototypeToSingletonMain
{
public static void main( String[] args )
{
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
X firstX = (X) ac.getBean("x");
Y firstY = firstX.getY();
X secondX = (X) ac.getBean("x");
Y secondY = secondX.getY();
System.out.println("do two prototype beans of type Y point to same object: "+firstY.equals(secondY));
ac.close();
}
}
6.在运行上面的程序时运行它,我们将得到以下输出。
Jun 15, 2015 1:32:58 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:32:58 IST 2015]; root of context hierarchy Jun 15, 2015 1:32:58 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype bean point to same object: true Jun 15, 2015 1:32:59 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:32:58 IST 2015]; root of context hierarchy
正如我们可以在此处注明的那样,即使我们已声明Bean Y作为原型,仍然表现得像Singleton bean一样。
解决方案
有很多方法可以解决这个问题。
使用方法注入
更新x.java如下。
package org.igi.theitroad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
public class X {
@Autowired
Y y;
@Lookup
public Y getY() {
return y;
}
public void setY(Y y) {
this.y = y;
}
}
我们可以使用方法注入来解决此问题。
将使用Gety()方法使用@Lookup,它将每次都返回新实例。
Spring将使用CGLIB动态生成一个覆盖方法Gety()的子类。
然后它将bean注册到应用程序上下文中。
每当我们要求Gety()方法时,它将返回y级的新实例。
这里有两个选项,创建虚拟方法或者标记方法作为抽象。
当我们将方法标记为摘要时,我们也必须声明类摘要,因此我们在此方案中使用了虚拟方法。
运行InjjectProTotyPetOineLateLonmain我们将得到以下输出:
Jun 15, 2016 1:51:38 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:51:38 IST 2016]; root of context hierarchy Jun 15, 2016 1:51:38 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false Jun 15, 2016 1:51:38 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:51:38 IST 2016]; root of context hierarchy
你现在可以注意到,我们现在正在得到两个不同的y实例。
使用ObjectFactory.
我们可以使用Objectory来获取新对象,每次调用gety()方法。
更新类A.java如下
package org.igi.theitroad;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class X {
@Autowired
private ObjectFactory<Y> prototypeBeanObjectFactory;
Y y;
public Y getY() {
return prototypeBeanObjectFactory.getObject();
}
public void setY(Y y) {
this.y = y;
}
}
运行InjjectProTotyPetOineLateLonmain我们将得到以下输出:
Jun 15, 2016 2:55:56 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:55:56 IST 2016]; root of context hierarchy Jun 15, 2016 2:55:57 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false Jun 15, 2016 2:55:58 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:55:56 IST 2016]; root of context hierarchy
使用ApplicationContextaware.
我们可以使用应用程序上下文返回新bean何时receven gety()方法已被调用。
更新类A.java如下
package org.igi.theitroad;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class X implements ApplicationContextAware{
private ApplicationContext applicationContext;
Y y;
public Y getY() {
return applicationContext.getBean(Y.class);
}
public void setY(Y y) {
this.y = y;
}
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
this.applicationContext=arg0;
}
}
运行InjjectProTotyPetOineLateLonmain我们将得到以下输出:
Jun 15, 2016 2:58:59 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:58:59 IST 2016]; root of context hierarchy Jun 15, 2016 2:58:59 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false Jun 15, 2016 2:59:00 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:58:59 IST 2016]; root of context hierarchy

