目录

scope源码解析

源码分析

Spring内常见的Scope有singleton,prototype,request,session;还有globalSession,application。如果用注解定义Bean的作用域,则可以使用**@Scope**,将@Scope标识在一个Bean的类上,就可以定义这个Bean在容器中的作用域,如果未标识则默认是singleton。对于不同的Scope类型的Bean,Spring区分对待。在实例化Bean时,先通过判断其BeanDefinition是不是singleton或者prototype,可以看BeanDefinition接口的代码:

Spring内常见的Scope有singleton,prototype,request,session;还有globalSession,application。如果用注解定义Bean的作用域,则可以使用**@Scope**,将@Scope标识在一个Bean的类上,就可以定义这个Bean在容器中的作用域,如果未标识则默认是singleton。对于不同的Scope类型的Bean,Spring区分对待。在实例化Bean时,先通过判断其BeanDefinition是不是singleton或者prototype,可以看BeanDefinition接口的代码:

1
2
3
4
5
6
7
8
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    ......
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    boolean isSingleton();
    boolean isPrototype();
    String getScope();
}

如果isSingleton() == true,则指明是singleton类型的Bean,只实例化一次,之后此Bean复用。

如果isPrototype() == true,则指明是prototype类型的Bean,每次获取这个Bean时都是创建一个新的对象。

如果以上两种情况均不符合,那么就需要用另外的逻辑来处理了,我们主要讲的就是这种情况。

Scope是一个接口(还有@Scope),当作用域是singleton或prototype时,没有用到这个接口,当作用域不是这两种类型时,就需要用这个接口了。可以先看Scope这个接口的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public interface Scope {
    //通过一个名称获取Bean实例
    Object get(String name, ObjectFactory<?> objectFactory);
    //销毁指定名称的Bean
    Object remove(String name);
    //为指定名称的Bean注册销毁时的回调函数
    void registerDestructionCallback(String name, Runnable callback);
    Object resolveContextualObject(String key);
    String getConversationId();
}

Scope接口有三个关键方法,获取Bean,销毁Bean,以及注册销毁时的回调函数,就相当于生成周期结束时的触发方法。

Spring容器在初始化的起始阶段,就会往BeanFactory内注册多个Scope的实例,看代码:

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#postProcessBeanFactory

1
2
3
4
5
6
7
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
   beanFactory.ignoreDependencyInterface(ServletContextAware.class);
  // 注册Scope实例
   registerWebApplicationScopes();
}

org.springframework.web.context.support.WebApplicationContextUtils#registerWebApplicationScopes(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext)

 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
/**
	 * Register web-specific scopes ("request", "session", "globalSession", "application")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 * @param beanFactory the BeanFactory to configure
	 * @param sc the ServletContext that we're running within
	 */
	public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
			@Nullable ServletContext sc) {

		beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
		beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
		if (sc != null) {
			ServletContextScope appScope = new ServletContextScope(sc);
			beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
			// 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);
		}
	}

在调用这步方法时,Spring在BeanFactory实例中注册了很多scope名称与Scope接口实现类的映射,可以查看AbstractBeanFactory的registerScope()方法。这些Scope实例中都有相应的通过Bean名称获取Bean实例的方法。同时注册了一些Bean的依赖实现,比如若Spring容器内注入ServletRequest,则会得到RequestObjectFactory实例,如果RequestObjectFactory实例是一个ObjectFactory接口的实现类,则返回getObject()方法得到的对象。

下面看当想要获取request或session作用域的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
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
 * Return an instance, which may be shared or independent, of the specified bean.
 * @param name the name of the bean to retrieve
 * @param requiredType the required type of the bean to retrieve
 * @param args arguments to use when creating a bean instance using explicit arguments
 * (only applied when creating a new instance as opposed to retrieving an existing one)
 * @param typeCheckOnly whether the instance is obtained for a type check,
 * not for actual use
 * @return an instance of the bean
 * @throws BeansException if the bean could not be created
 */
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   ...

      try {
         ...

         // Create bean instance.
         if (mbd.isSingleton()) {
            ...
         }

         else if (mbd.isPrototype()) {
            ...
         }

         else {
           //当既不是singleton也不是prototype时
            String scopeName = mbd.getScope();
           //通过scope的名称获取注册的实例,Scope接口的实现类,在上文中注册的
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
              // 通过Scope实例获取Bean
               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);
            }
         }
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
   }

   // Check if required type matches the type of the actual bean instance.
   if (requiredType != null && !requiredType.isInstance(bean)) {
      try {
         T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
         if (convertedBean == null) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
         }
         return convertedBean;
      }
      catch (TypeMismatchException ex) {
         if (logger.isTraceEnabled()) {
            logger.trace("Failed to convert bean '" + name + "' to required type '" +
                  ClassUtils.getQualifiedName(requiredType) + "'", ex);
         }
         throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
   }
   return (T) bean;
}

Request和Session作用域对应的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
public abstract class AbstractRequestAttributesScope implements Scope {

   @Override
   public Object get(String name, ObjectFactory<?> objectFactory) {
      RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
      Object scopedObject = attributes.getAttribute(name, getScope());
     // 如果这个对象还不存在,则通过objectFactory生成一个,然后存入RequestAttributes 中
      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;
   }

   @Override
   @Nullable
   public Object remove(String name) {
      RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
      Object scopedObject = attributes.getAttribute(name, getScope());
      if (scopedObject != null) {
         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();
      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();

}

以上的方法有个关键点,从RequestContextHolder获取当前的RequestAttributes ,然后再从RequestAttributes 里获取相应的Bean,或者删除相应的Bean。RequestContextHolder,从字面理解为请求上下文的持有者,可以通过它获取到当前请求。下面就看RequestContextHolder的如何获取到RequestAttributes 及获取到的RequestAttributes 是什么。

  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
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public abstract class RequestContextHolder  {

   private static final boolean jsfPresent =
         ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

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

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


   /**
    * Reset the RequestAttributes for the current thread.
    */
   public static void resetRequestAttributes() {
      requestAttributesHolder.remove();
      inheritableRequestAttributesHolder.remove();
   }

   /**
    * Bind the given RequestAttributes to the current thread,
    * <i>not</i> exposing it as inheritable for child threads.
    * @param attributes the RequestAttributes to expose
    * @see #setRequestAttributes(RequestAttributes, boolean)
    */
   public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
      setRequestAttributes(attributes, false);
   }

   /**
    * Bind the given RequestAttributes to the current thread.
    * @param attributes the RequestAttributes to expose,
    * or {@code null} to reset the thread-bound context
    * @param inheritable whether to expose the RequestAttributes as inheritable
    * for child threads (using an {@link InheritableThreadLocal})
    */
    //将一个RequestAttributes 与当前线程绑定
   public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
      if (attributes == null) {
         resetRequestAttributes();
      }
      else {
         if (inheritable) {
            inheritableRequestAttributesHolder.set(attributes);
            requestAttributesHolder.remove();
         }
         else {
            requestAttributesHolder.set(attributes);
            inheritableRequestAttributesHolder.remove();
         }
      }
   }

   /**
    * Return the RequestAttributes currently bound to the thread.
    * @return the RequestAttributes currently bound to the thread,
    * or {@code null} if none bound
    */
  //获取当前现场绑定的RequestAttributes 
   @Nullable
   public static RequestAttributes getRequestAttributes() {
      RequestAttributes attributes = requestAttributesHolder.get();
      if (attributes == null) {
         attributes = inheritableRequestAttributesHolder.get();
      }
      return attributes;
   }

   /**
    * Return the RequestAttributes currently bound to the thread.
    * <p>Exposes the previously bound RequestAttributes instance, if any.
    * Falls back to the current JSF FacesContext, if any.
    * @return the RequestAttributes currently bound to the thread
    * @throws IllegalStateException if no RequestAttributes object
    * is bound to the current thread
    * @see #setRequestAttributes
    * @see ServletRequestAttributes
    * @see FacesRequestAttributes
    * @see javax.faces.context.FacesContext#getCurrentInstance()
    */
  //获取当前线程绑定的RequestAttributes ,如果RequestAttributes 为null,说明没有HttpRequest请求线程存在,
    //也就是说当前容器还在初始化阶段,在容器初始化阶段就尝试实例化一个与HttpRequest绑定的Bean会报错
    //也就说scope为request或session的Bean不能在容器初始化阶段实例化,不能直接注入scope为singleton的Bean中
   public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
      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;
   }


   /**
    * Inner class to avoid hard-coded JSF dependency.
    */
   private static class FacesRequestAttributesFactory {

      @Nullable
      public static RequestAttributes getFacesRequestAttributes() {
         FacesContext facesContext = FacesContext.getCurrentInstance();
         return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
      }
   }

}

RequestContextHolder 有两个ThreadLocal类型的静态成员,专门用于存储Http请求的信息。如果使用的是SpringMVC作为接受Http请求的框架,可以如下代码:

 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
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ......
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
        ......
        initContextHolders(request, localeContext, requestAttributes);
        ......
        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
        }
    }

    //通过HttpServletRequest ,HttpServletResponse 生成RequestAttributes 实例
    protected ServletRequestAttributes buildRequestAttributes(
            HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {

        if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
            return new ServletRequestAttributes(request, response);
        }
        else {
            return null;  // preserve the pre-bound RequestAttributes instance
        }
    }

    private void initContextHolders(
            HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

        if (localeContext != null) {
            LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
        }
        //将当前Http请求的Request,Response的相关信息设置到ThreadLocal对象中。
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Bound request context to thread: " + request);
        }
    }
    //在当前请求结束时,还原ThreadLocal的线程绑定对象,清空此次请求的信息
    private void resetContextHolders(HttpServletRequest request,
            LocaleContext prevLocaleContext, RequestAttributes previousAttributes) {

        LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
        RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
        if (logger.isTraceEnabled()) {
            logger.trace("Cleared thread-bound request context: " + request);
        }
    }

}

也就是说每接收到一个Http请求,将请求的相关HttpServletReqeust,HttpServletResponse封装成一个ServletRequestAttributes 对象,和当前线程绑定,赋值到RequestContextHolder的ThreadLocal静态成员中。下面看ServletRequestAttributes 对象内的关键方法:

 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
public class ServletRequestAttributes extends AbstractRequestAttributes {

    private final HttpServletRequest request;
    private HttpServletResponse response;
    private volatile HttpSession session;

    //根据作用域的值将从request还是session中获取指定名称的对象
    public Object getAttribute(String name, int scope) {
        if (scope == SCOPE_REQUEST) {
            if (!isRequestActive()) {
                throw new IllegalStateException(
                        "Cannot ask for request attribute - request is not active anymore!");
            }
            return this.request.getAttribute(name);
        }
        else {
            HttpSession session = getSession(false);
            if (session != null) {
                try {
                    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;
        }
    }
    //根据作用域的值将指定对象存储到request还是session中
    public void setAttribute(String name, Object value, int scope) {
        if (scope == SCOPE_REQUEST) {
            if (!isRequestActive()) {
                throw new IllegalStateException(
                        "Cannot set request attribute - request is not active anymore!");
            }
            this.request.setAttribute(name, value);
        }
        else {
            HttpSession session = getSession(true);
            this.sessionAttributesToUpdate.remove(name);
            session.setAttribute(name, value);
        }
    }

}

也就是说ServletRequestAttributes 在存储Bean时根据scope的值存入request或者session中,在获取Bean根据scope的值从request或者session中获取。

scope为request和session的Bean可以定义销毁时的方法,如用注解@PreDestroy定义,那么在request结束或者session结束时,会调用定义的方法作为其生命周期的一部分。Spring对request和session作用域的Bean负责其生命周期的处理。

总结

  1. 可以通过@Scope注解来指定Bean的作用域,若未指定默认是singleton
  2. Spring注册了一些Scope接口的实例用于处理scope非singleton和prototype的Bean
  3. Scope实例从Reqeust中存取scope=request的Bean,从Session中存取scope=session的Bean
  4. 每接收到一个Http请求时,将HttpServletRequest和HttpServletResponse封装成一个ServletRequestAttributes 绑定到RequestContextHolder的ThreadLocal变量中,在请求结束时清除绑定
  5. Scope从RequestContextHolder中获取当前线程的ServletRequestAttributes ,然后从其内的HttpServletRequest存入或取出或者删除指定名称及作用域的Bean对象。