在单例Bean中依赖原型Bean会有什么问题?如何解决?

Talk is cheap. Show me the code

第一步:创建一个单例Bean。

package com.xxx.tech.scope;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;

/***
 * 定义一个单例Bean,依赖原型Bean{@link PrototypeBean}
 * @author 君战
 * **/
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SingletonBean {
     

	// 依赖原型Bean
	@Autowired
	private PrototypeBean prototypeBean;

	// 打印注入的原型Bean 的hashcode来查看每次获取到的原型Bean是否是同一个
	public void display() {
     
		System.out.println("原型Bean hashcode为:" + prototypeBean.hashCode());
	}
}

第二步:创建一个原型Bean。

package com.xxx.tech.scope;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;

/***
 * 定义一个原型Bean,被单例Bean{@link SingletonBean}依赖
 * @author 君战
 * **/
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
     
}

第三步:编写启动类,用来测试。

package com.xxx.tech.scope;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/***
 * 演示单例Bean依赖原型Bean 问题
 * @author 君战
 * **/
public class SingletonDependencyPrototypeBeanDemo {
     

	public static void main(String[] args) {
     
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(SingletonBean.class,PrototypeBean.class);
		context.refresh();
		SingletonBean singletonBean = context.getBean(SingletonBean.class);
		// 调用两次 SingletonBean#display方法,我们的期望是两次打印的原型Bean的hashcode是不一样的
		singletonBean.display();
		singletonBean.display();
	}


}

第四步:执行启动类main方法,查看控制台。

原型Bean hashcode为:1218593486
原型Bean hashcode为:1218593486

Process finished with exit code 0

可以看到原型Bean上的@Scope注解仿佛失去了效果,在单例Bean每次调用display方法打印的原型Bean的hashcode都是一致的。

这种现象虽然在意料之外,但是在情理之中。因为原型Bean只会被实例化、初始化一次,因此其依赖也只会被处理一次,所以被注入的原型Bean也隐式的成为了单例Bean。那么如何解决这个问题呢?有两种方法,一种是依赖查找,另一种是使用抽象方法来解决(官方推荐),接下来就代码分别演示下这两种方式。

依赖查找解决单例Bean中依赖原型Bean问题

修改SingletonBean类,添加一个ApplicationContext依赖,然后修改display方法,每次执行都通过ApplicationContext的getBean方法重新获取PrototypeBean。

package com.xxx.tech.scope;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;

/***
 * 定义一个单例Bean,依赖原型Bean{@link PrototypeBean}
 * @author 君战
 * **/
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SingletonBean {
     

	// 依赖原型Bean
	@Autowired
	private PrototypeBean prototypeBean;
	
	@Autowired
	private ApplicationContext applicationContext;

	// 打印注入的原型Bean 的hashcode来查看每次获取到的原型Bean是否是同一个
	public void display() {
     
		System.out.println("原型Bean hashcode为:" + applicationContext.getBean(PrototypeBean.class).hashCode());
	}

}

抽象方法解决单例Bean中依赖原型Bean问题(官方推荐)

首先给出官方文档地址,在官方文档是也有Demo。抽象方法解决单例Bean中依赖原型Bean问题

将SingletonBean声明为抽象类,定义一个抽象方法,方法名是什么无所谓,只需要将该方法的返回值声明为PrototypeBean即可。

package com.xxx.tech.scope;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;

/***
 * 定义一个单例Bean,依赖原型Bean{@link PrototypeBean}
 * @author 君战
 * **/
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public abstract class SingletonBean {
     

	@Lookup
	protected abstract PrototypeBean createPrototypeBean();


	// 打印注入的原型Bean 的hashcode来查看每次获取到的原型Bean是否是同一个
	public void display() {
     
		System.out.println("原型Bean hashcode为:" + createPrototypeBean().hashCode());
	}

}

再次执行启动类的main方法,查看控制台,可以发现可以做到和依赖查找一样的效果。


原型Bean hashcode为:1763344271
原型Bean hashcode为:1353170030

Process finished with exit code 0

官方文档-1.4.6小节。在单例Bean中依赖原型Bean会有什么问题?如何解决?_第1张图片

总结

在单例Bean中依赖原型Bean时,由于单例Bean只会被IoC容器初始化一次,其依赖也只会被处理一次,因此其依赖的原型Bean也“隐式”的称为单例。

如何解决这个问题,有两种办法,一种是在使用原型Bean时每次都依赖查找,这样IoC容器会每次都重新创建原型Bean;另一种办法就是使用@Lookup注解来解决,这种是官方给出解决方案,需注意的是使用@Lookup注解的方法必须声明为抽象方法。

你可能感兴趣的:(Spring,Context,spring,bean,java,spring,boot,spring,cloud)