在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