Spring中Bean会被缓存吗?Spring的Bean是如何被缓存的?

一、demo实例

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;

/**
 * Bean 是否缓存示例
 */
public class BeanCachingDemo {
    
    

    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class
        context.register(BeanCachingDemo.class);

        // 启动 Spring 应用上下文
        context.refresh();

        // BeanCachingDemo 是 Configuration Class,Singleton Scope Bean
        BeanCachingDemo beanCachingDemo = context.getBean(BeanCachingDemo.class);

        for (int i = 0; i < 9; i++) {
    
    
            // Singleton Scope Bean 是存在缓存
            System.out.println(beanCachingDemo == context.getBean(BeanCachingDemo.class));
        }

        User user = context.getBean(User.class);

        for (int i = 0; i < 9; i++) {
    
    
            // Prototype Scope Bean
            System.out.println(user == context.getBean(User.class));
        }

        // 关闭 Spring 应用上下文
        context.close();
    }

    @Bean
    @Scope("prototype") // 原型 scope
    public static User user() {
    
    
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        return user;
    }
}

由结果我们可以看出,每次获取的单例Bean,都是相同的;每次获取的原型Bean,每次都是不同的。

二、单例Bean(Singleton)会被缓存

1、当我们调用getBean()方法时,会执行以下逻辑:
resolveNamedBean方法中会调用getBean方法,最终会执行org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
    
	// ...
			// Create bean instance.
			if (mbd.isSingleton()) {
    
    
				// 获取单实例bean
				sharedInstance = getSingleton(beanName, () -> {
    
    
					try {
    
    
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
    
    
						// Explicitly remove instance from singleton cache: It might have been put there
						// eagerly by the creation process, to allow for circular reference resolution.
						// Also remove any beans that received a temporary reference to the bean.
						destroySingleton(beanName);
						throw ex;
					}
				});
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
			}
	// ...
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    
    
	Assert.notNull(beanName, "Bean name must not be null");
	synchronized (this.singletonObjects) {
    
    
		Object singletonObject = this.singletonObjects.get(beanName); // 缓存,如果有直接返回
		if (singletonObject == null) {
    
    
			if (this.singletonsCurrentlyInDestruction) {
    
    
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			if (logger.isDebugEnabled()) {
    
    
				logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
			}
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
    
    
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
    
    
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
    
    
				// Has the singleton object implicitly appeared in the meantime ->
				// if yes, proceed with it since the exception indicates that state.
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
    
    
					throw ex;
				}
			}
			catch (BeanCreationException ex) {
    
    
				if (recordSuppressedExceptions) {
    
    
					for (Exception suppressedException : this.suppressedExceptions) {
    
    
						ex.addRelatedCause(suppressedException);
					}
				}
				throw ex;
			}
			finally {
    
    
				if (recordSuppressedExceptions) {
    
    
					this.suppressedExceptions = null;
				}
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
    
     // 如果是true,会被缓存
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
// 此处的singletonObjects 就是单例bean的缓存,它是一个ConcurrentHashMap
protected void addSingleton(String beanName, Object singletonObject) {
    
    
	synchronized (this.singletonObjects) {
    
    
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

三、原型Bean(Prototype)不会缓存

原型Bean每次获取都会创建新的bean,并不会缓存。

// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
else if (mbd.isPrototype()) {
    
    
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
    
    
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
    
    
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

四、其他Scope Bean

  • request:每个ServletRequest内部缓存,生命周期维持在每次HTTP请求。
  • session:每个HttpSession内部缓存,生命周期维持在每个用户HTTP会话。
  • application:当前Serviet应用内部缓存。

1、我们继续看else判断逻辑

// // org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
else {
    
    
	String scopeName = mbd.getScope();
	final Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
    
    
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
    
    
		// 调用了scope的get方法
		Object scopedInstance = scope.get(beanName, () -> {
    
    
			beforePrototypeCreation(beanName);
			try {
    
    
				return createBean(beanName, mbd, args);
			}
			finally {
    
    
				afterPrototypeCreation(beanName);
			}
		});
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
	catch (IllegalStateException ex) {
    
    
		throw new BeanCreationException(beanName,
				"Scope '" + scopeName + "' is not active for the current thread; consider " +
				"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
				ex);
	}
}

关于scope,我们可以查看该文章:
详解SpringBean的作用域(Scopes)

2、我们发现,request和session的scope,都继承了AbstractRequestAttributesScope,我们继续查看其get方法

我们发现是用attribute来实现的。

// org.springframework.web.context.request.AbstractRequestAttributesScope#get
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
    
    
	RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
	Object scopedObject = attributes.getAttribute(name, getScope());
	if (scopedObject == null) {
    
    
		scopedObject = objectFactory.getObject();
		attributes.setAttribute(name, scopedObject, getScope());
		// Retrieve object again, registering it for implicit session attribute updates.
		// As a bonus, we also allow for potential decoration at the getAttribute level.
		Object retrievedObject = attributes.getAttribute(name, getScope());
		if (retrievedObject != null) {
    
    
			// Only proceed with retrieved object if still present (the expected case).
			// If it disappeared concurrently, we return our locally created instance.
			scopedObject = retrievedObject;
		}
	}
	return scopedObject;
}

3、而application的scope,是基于ServletContext来实现的:

// org.springframework.web.context.support.ServletContextScope#get
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
    
    
	Object scopedObject = this.servletContext.getAttribute(name);
	if (scopedObject == null) {
    
    
		scopedObject = objectFactory.getObject();
		this.servletContext.setAttribute(name, scopedObject);
	}
	return scopedObject;
}

猜你喜欢

转载自blog.csdn.net/A_art_xiang/article/details/128466494