关于Spring Bean的作用域基本概念和使用方式,之前的文章已经做过详细介绍。这篇文章我们主要介绍Bean作用域的底层实现。

首先我们知道Bean的作用域会影响Spring创建Bean的方式,那我们先来看Spring是如何创建Bean对象的,主要代码在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// 省略无关代码

@SuppressWarnings("unchecked")
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()) {
// 创建singleton作用域的bean
}

else if (mbd.isPrototype()) {
// 创建prototype作用域的bean
}

else {
// 创建注册的Scope作用域的bean
}
}
}

对于不同作用域的beanSpring分情况处理。在doGetBean方法中,先根据BeanDefinition判断是否为singleton或者prototype,如果都不是则处理通过注册机制实现的作用域。由此可见singletonprototypeSpring通过BeanDefinition内置的作用域,而像requestsession等作用域是通过注册机制实现的,因为它们只适用于Web环境。

判断BeanDefinition是否为singleton或者prototype作用域的原理很简单,直接使用字符串的equals方法进行比较,主要代码在其抽象实现类AbstractBeanDefinition中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {

/**
* Constant for the default scope name: {@code ""}, equivalent to singleton
* status unless overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";

@Nullable
private String scope = SCOPE_DEFAULT;

/**
* Return whether this a <b>Singleton</b>, with a single shared instance
* returned from all calls.
* @see #SCOPE_SINGLETON
*/
@Override
public boolean isSingleton() {
// "singleton".equals(this.scope) || "".equals(this.scope)
return SCOPE_SINGLETON.equals(this.scope) || SCOPE_DEFAULT.equals(this.scope);
}

/**
* Return whether this a <b>Prototype</b>, with an independent instance
* returned for each call.
* @see #SCOPE_PROTOTYPE
*/
@Override
public boolean isPrototype() {
// "prototype".equals(this.scope)
return SCOPE_PROTOTYPE.equals(this.scope);
}
}

这里我们主要介绍通过注册机制实现的作用域,即requestsession作用域。首先需要知道org.springframework.beans.factory.config.Scope这个接口,使用注册机制的作用域类都需要实现该接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public interface Scope {

/**
* 根据bean名称获取bean实例
*/
Object get(String name, ObjectFactory<?> objectFactory);

/**
* 销毁指定name的bean
*/
@Nullable
Object remove(String name);

/**
* 给指定name的bean注册销毁时的回调函数
*/
void registerDestructionCallback(String name, Runnable callback);

/**
* 解析给定key的上下文对象(如果有)
*/
@Nullable
Object resolveContextualObject(String key);

/**
* 可选,返回conversationId
*/
@Nullable
String getConversationId();
}

主要包含三个方法:获取bean、销毁bean和销毁时的回调。

Spring容器在初始化过程中,会向BeanFactory中注册多个Scope接口的实现类,主要代码在org.springframework.web.context.support.WebApplicationContextUtils#registerWebApplicationScopes方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class WebApplicationContextUtils {
// 省略无关代码

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
@Nullable ServletContext sc) {
// 注册request作用域
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
// 注册session作用域
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
// 注册scope作用域
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}

beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
}

beanFactory.registerScope方法的实现在org.springframework.beans.factory.support.AbstractBeanFactory抽象类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// 省略无关代码

/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);

@Override
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
// "singleton" || "prototype" 不允许注册
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
// 注册scope:放入LinkedHashMap,如果scopeName已存在则进行覆盖,返回旧的Scope,下面打日志提示。
Scope previous = this.scopes.put(scopeName, scope);
if (previous != null && previous != scope) {
if (logger.isDebugEnabled()) {
logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
}

这时我们再回过头来看AbstractBeanFactory#doGetBean方法中创建注册的作用域的bean的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// 省略无关代码

@SuppressWarnings("unchecked")
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()) {
// 创建singleton作用域的bean
}
else if (mbd.isPrototype()) {
// 创建prototype作用域的bean
}
else {
// 创建注册的Scope作用域的bean

// 获取BeanDefinition中定义的Scope属性名
String scopeName = mbd.getScope();
// 空校验
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
}
// 从LinkedHashMap中获取对应Scope实现类。前面registerScope方法中set放入的
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);
}
}
}
}

关于bean对象的创建过程我们这里不展开讨论,只需要知道这里调用了scope对象的get方法获取bean对象。

下面我们介绍requestsession作用域的实现。这两个作用域的实现类org.springframework.web.context.request.RequestScopeorg.springframework.web.context.request.SessionScope都继承自org.springframework.web.context.request.AbstractRequestAttributesScope抽象类,该抽象类实现了Scope接口。下面是在AbstractRequestAttributesScope抽象类中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public abstract class AbstractRequestAttributesScope implements Scope {

@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// 获取RequestAttributes对象
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// 从RequestAttributes对象中获取name对应的bean实例,这里getAttribute方法中根据传入的第二个参数getScope()做不同的处理
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
// 如果bean对象还不存在,则从对象工厂objectFactory中获取
scopedObject = objectFactory.getObject();
// 存储创建好的bean对象,setAttribute方法会根据传入的getScope()值做不同处理
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;
}
}
}

@Override
@Nullable
public Object remove(String name) {
// 获取RequestAttributes对象并从Attribute中获取指定name的bean对象
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject != null) {
// 存在则remove
attributes.removeAttribute(name, getScope());
return scopedObject;
}
else {
return null;
}
}

@Override
public void registerDestructionCallback(String name, Runnable callback) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// 注册回调
attributes.registerDestructionCallback(name, callback, getScope());
}

@Override
@Nullable
public Object resolveContextualObject(String key) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// 获取key引用
return attributes.resolveReference(key);
}


/**
* 模板方法设计模式的体现
*
* Template method that determines the actual target scope.
* @return the target scope, in the form of an appropriate
* {@link RequestAttributes} constant
* @see RequestAttributes#SCOPE_REQUEST
* @see RequestAttributes#SCOPE_SESSION
*/
protected abstract int getScope();

}

可以看到该抽象类使用了模板方法设计模式,抽象方法getScope()是模板方法,由子类RequestScopeSessionScope进行实现,抽象类中对Scope接口中方法的实现主要都是通过调用RequestContextHolder.currentRequestAttributes();方法获取到RequestAttributes类对象,然后调用相应方法。例如get方法中:

1
2
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());

RequestContextHolder类从类名看是请求上下文持有者,调用currentRequestAttributes()方法得到了RequestAttributes类的实例,那我们就先来看RequestContextHolder类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public abstract class RequestContextHolder  {
// 省略无关代码

// ThreadLocal
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");

// InheritableThreadLocal
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");

@Nullable
public static RequestAttributes getRequestAttributes() {
// 先从ThreadLocal中获取,为null再从InheritableThreadLocal中获取
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}

public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
// 获取和当前线程绑定的RequestAttributes对象
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {//
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
throw new IllegalStateException("No thread-bound request found: " +
"Are you referring to request attributes outside of an actual web request, " +
"or processing a request outside of the originally receiving thread? " +
"If you are actually operating within a web request and still receive this message, " +
"your code is probably running outside of DispatcherServlet: " +
"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
}
}
return attributes;
}
}

currentRequestAttributes()方法从ThreadLocalInheritableThreadLocal对象中获取了和当前线程绑定的RequestAttributes对象,然后调用getAttribute方法获取bean实例,这里暂且先不关心RequestAttributes对象是何时被设置到ThreadLocal当中的,我们先来看下RequestAttributes类的实现。RequestAttributes类是一个接口,Spring提供了一个抽象实现AbstractRequestAttributes,其主要实现类是ServletRequestAttributes,其中getAttribute方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class ServletRequestAttributes extends AbstractRequestAttributes {
// 省略无关代码

private final HttpServletRequest request;

@Override
public Object getAttribute(String name, int scope) {
// RequestAttributes#SCOPE_REQUEST == 0
if (scope == SCOPE_REQUEST) {// request作用域
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attribute - request is not active anymore!");
}
// 从HttpServletRequest对象中获取name对应的bean对象
return this.request.getAttribute(name);
}
else {// session作用域
// 获取HttpSession对象
HttpSession session = getSession(false);
if (session != null) {
try {
// 从HttpSession对象中获取name对应的bean对象
Object value = session.getAttribute(name);
if (value != null) {
this.sessionAttributesToUpdate.put(name, value);
}
return value;
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return null;
}
}
}

如果传入的scope参数等于0则是request作用域,从HttpServletRequest对象中获取name对应的bean对象;否则一定是session作用域,获取HttpSession对象并从中获取name对应的bean对象。

传入的scope参数是上文中提到的getScope()抽象模板方法,由子类RequestScopeSessionScope提供实现:

1
2
3
4
5
6
7
8
9
public class RequestScope extends AbstractRequestAttributesScope {
// 省略无关代码

@Override
protected int getScope() {
// SCOPE_REQUEST = 0
return RequestAttributes.SCOPE_REQUEST;
}
}
1
2
3
4
5
6
7
8
9
public class SessionScope extends AbstractRequestAttributesScope {
// 省略无关代码

@Override
protected int getScope() {
// SCOPE_SESSION = 1
return RequestAttributes.SCOPE_SESSION;
}
}

其它使用到的方法如setAttributeremoveAttributeregisterDestructionCallbackresolveReference的实现思路也是根据传入的scope参数是否等于0来做不同处理。例如setAttribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ServletRequestAttributes extends AbstractRequestAttributes {
// 省略无关代码

private final HttpServletRequest request;

@Override
public void setAttribute(String name, Object value, int scope) {
// RequestAttributes#SCOPE_REQUEST == 0
if (scope == SCOPE_REQUEST) {// request作用域
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot set request attribute - request is not active anymore!");
}
// 将name->value设置到HttpServletRequest中
this.request.setAttribute(name, value);
}
else {// session作用域
// 获取session
HttpSession session = obtainSession();
this.sessionAttributesToUpdate.remove(name);
// 将name->value设置到HttpSession中
session.setAttribute(name, value);
}
}
}

至此,Scope作用域的原理基本介绍完毕。