spring的作用域限定了Spring Bean的作用范围,在Spring配置文件定义Bean时,通过声明scope属性配置项,可以灵活定义Bean的作用范围。例如,当你希望每次IOC容器返回的Bean是同一个实例时,可以设置scope为singleton;当你希望每次IOC容器返回的Bean实例是一个新的实例时,可以设置scope为prototype。
scope配置项有5个属性,用于描述不同的作用域。
使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。
使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。
该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。
该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。
当你在看spring主流程的代码时,你会走读到AbstractBeanFactory这个类的doGetBean方法,在这个方法中
// Create bean instance.
if (mbd.isSingleton()) {//单例作用域下
sharedInstance = getSingleton(beanName, new ObjectFactory
其实Singleton和Prototype的条件下他们都是调用一样的createBean方法,不同的是Singleton会再创建好了bean之后会放到单例缓存中,下次就直接获取了。
这里主要分析下其他作用域下是怎么创建bean的:
1.mbd.getScope();这个方法的意思是从beandefinition中获取作用域,其实就是我们在xm文件中配置的:
2.this.scopes.get(scopeName);这个方法是从map中获取实现了Scope接口的对象,我看了下有好几个
分别对应这个request、session、globalSession、application
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
beanFactory.registerScope("request", new RequestScope());
beanFactory.registerScope("session", new SessionScope(false));
beanFactory.registerScope("globalSession", new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope("application", appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
beanFactory.registerResolvableDependency(ServletRequest.class, new WebApplicationContextUtils.RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new WebApplicationContextUtils.ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new WebApplicationContextUtils.SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebApplicationContextUtils.WebRequestObjectFactory());
if (jsfPresent) {
WebApplicationContextUtils.FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
发现scopes这个map对象是AbstractBeanFactory的registerScope方法
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
Scope previous = this.scopes.put(scopeName, scope);
if (previous != null && previous != scope) {
if (logger.isInfoEnabled()) {
logger.info("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
既然是beanFactory的方法,我突然想到接口BeanFactoryPostProcessor有一个方法可以获得beanFactory的实例,我们用这个实例去调用注册作用域不就可以把自己定义的作用域注册进去。
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
3.作用域的scope.get方法是需要我们为自己的作用定义逻辑的地方,根据自己的逻辑判断去创建bean(即调用ObjectFactory的getObject()方法)
创建自定义的作用域类,每访问5次创建一个新实例
public class LzlScope implements Scope {
static AtomicInteger count = new AtomicInteger(0);
static ConcurrentHashMap cache = new ConcurrentHashMap<>(64);
@Override
public Object get(String name, ObjectFactory> objectFactory) {
Object result = null;
int temp = count.getAndIncrement();
if(temp%5 == 0){
result = objectFactory.getObject();
cache.put(name,result);
}
if(null == result){
result = cache.get(name);
}
return result;
}
@Override
public Object remove(String name) {
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
注册自定义作用域
public class LzlBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("lzlScope",new LzlScope());
beanFactory.createBean(Test.class);
}
}
创建一个bean类
public class LzlBean {
static AtomicInteger integer = new AtomicInteger(0);
private String name;
private String id;
public LzlBean(){
System.out.println("instance has create="+integer.incrementAndGet());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
创建自定义作用域的bean配置
测试作用域代码
public class Test {
public static int x=10;
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
for(int i=0;i<20;i++){
System.out.println(context.getBean("lzlBean"));
}
// System.out.println(context==LzlContext.context);
// Test s= (Test)context.getBean("com.lzl.springscope.LzlBeanFactoryPostProcessor#0");
// System.out.println((s).x);
context.close();
}
}
测试结果:
九月 02, 2019 6:01:52 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@685f4c2e: startup date [Mon Sep 02 18:01:52 CST 2019]; root of context hierarchy
九月 02, 2019 6:01:52 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [application.xml]
instance has create=1
com.lzl.springscope.LzlBean@5056dfcb
com.lzl.springscope.LzlBean@5056dfcb
com.lzl.springscope.LzlBean@5056dfcb
com.lzl.springscope.LzlBean@5056dfcb
com.lzl.springscope.LzlBean@5056dfcb
instance has create=2
com.lzl.springscope.LzlBean@6574b225
com.lzl.springscope.LzlBean@6574b225
com.lzl.springscope.LzlBean@6574b225
com.lzl.springscope.LzlBean@6574b225
com.lzl.springscope.LzlBean@6574b225
instance has create=3
com.lzl.springscope.LzlBean@2669b199
com.lzl.springscope.LzlBean@2669b199
com.lzl.springscope.LzlBean@2669b199
com.lzl.springscope.LzlBean@2669b199
com.lzl.springscope.LzlBean@2669b199
instance has create=4
com.lzl.springscope.LzlBean@2344fc66
com.lzl.springscope.LzlBean@2344fc66
com.lzl.springscope.LzlBean@2344fc66
com.lzl.springscope.LzlBean@2344fc66
com.lzl.springscope.LzlBean@2344fc66
九月 02, 2019 6:01:53 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@685f4c2e: startup date [Mon Sep 02 18:01:52 CST 2019]; root of context hierarchy
很明显,自定义作用域是可行的。