在Spring中将原型bean注入到单例bean中

时间:2020-02-23 14:35:41  来源:igfitidea点击:

问题

当我们将原型bean注入单例bean时,原型bean仍然表现得像单例bean。

让我们看个例子

  1. 创建一个简单的Java Maven项目。
  2. 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");
	}
	
}
  1. 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