Spring注解@Scope---SessionScope和RequestScope

前言

前两天分析了一波@ComponentScan注解各属性作用,通过源码看到根据类上的@Scope注解为BeanDefinition设置了ScopedProxyMode属性,并对BeanDefinition进行代理操作。大概就是如果这个BeanDefinition有@Scope,就会new一个BeanDefinition将原先的BeanDefition替换,具体看上篇文吧。注意新创建的BeanDefinition的beanClass是ScopedProxyFactoryBean,是一个FactoryBean相关代码如下

// 创建一个新的BeanDefition来作为代理的beanDefition,把原先的对象作为成员设置进去
		RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
		proxyDefinition.setOriginatingBeanDefinition(targetDefinition);

Scope常见的在Web应用中有session、request、application,还有cloud里边的@RefreshScope等。

一个一个来吧,以后碰到哪个分析完再接到这篇上。

@SessionScope和@RequestScope

每个Session或每个Request创建一个对象

先来看获取Bean对象的代码

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

                // 不相关代码都删了
				if (mbd.isSingleton()) {
                    // 获取单例bean实例
				}
				else if (mbd.isPrototype()) {
				   // 获取原型模式实例
				}
				else {
                    // 获取BeanDefinition的scope,即session\request
					String scopeName = mbd.getScope();
					// 这个集合中保存了这个字符串“session”对应的哪个Scope对象,用这个Scope对象来进行创建操作
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
                        // 获取被代理过的BeanDefition对应的FactoryBean对象
                        // 先进去看看对应Scope对象的get操作是什么
                        // createBean方法不用看了,就是根据bean定义创建实例,以前看过,这里不是目标就略过,知道这当作一个参数传进scope.get就可以了
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
                                
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});

                        // FactoryBean.getObject来获取真正的bean实例
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						// 报异常
					}

beanFactory中的this.scopes集合怎么来的呢

AbstractApplicationContext的refresh方法的postProcessBeanFactory()这一步添加的,AbstractApplicationContext这里只是模板方法,具体实现在Web容器的实现类AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory

	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		super.postProcessBeanFactory(beanFactory);
		if (this.basePackages != null && this.basePackages.length > 0) {
			this.scanner.scan(this.basePackages);
		}
		if (!this.annotatedClasses.isEmpty()) {
			this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
		}
	}

父类方法org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#postProcessBeanFactory

	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		registerWebApplicationScopes();
	}


	private void registerWebApplicationScopes() {
		ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
		WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
		existingScopes.restore();
	}


// 直接跟到WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
	public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
			@Nullable ServletContext sc) {

		beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
		beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
}

那此时beanFactory有了session和request的Scope对象,来看SessionScope的get方法

org.springframework.web.context.request.SessionScope#get

@Override
	public Object get(String name, ObjectFactory objectFactory) {
		Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
		synchronized (mutex) {
			return super.get(name, objectFactory);
		}
	}


// 进父类
	@Override
	public Object get(String name, ObjectFactory objectFactory) {
        // 从ThreadLocal变量中获取当前request的属性对象
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		
        // 从属性中根据bean名获取bean对象
        Object scopedObject = attributes.getAttribute(name, getScope());
		if (scopedObject == null) {
            // 如果没有会调用CreateBean方法新建一个,并设置到当前请求上下文属性中
			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;
	}

跟进去再看看RequestAttributes这个请求属性对象哪来的

public abstract class RequestContextHolder  {

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

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


	public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}
}

从请求对象RequestAttribute中根据beanName获取实例代码

	@Override
	public Object getAttribute(String name, int scope) {
        // scope是request
		if (scope == SCOPE_REQUEST) {
            // 如果请求已经处理完毕,判断了一个boolean值,下边有介绍
			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;
		}
	}

 好家伙,又来新知识了,这个RequestAttribute对象很关键,从线程ThreadLocal变量获取的,这可以理解,一个请求一个线程来处理嘛,那这个对象啥时候被设置进去的呢。思考了下,估计是DispatchServlet接受请求分发的时候处理,我们继续跟代码

DispatchServlet那肯定先看service(),先走爷爷类HttpServlet#service,这里没有,再看父类FrameworkServlet#service,有了

org.springframework.web.servlet.FrameworkServlet#service

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            // 这里
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        // 忽略了无关代码

        // 获取原先的RequestAttribute
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // new一个新的
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        // 设置到RequestContextHolder的ThreadLocal中
		initContextHolders(request, localeContext, requestAttributes);

	    // diService()执行
	try {
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
             // 重新设置回原来的
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
                // 请求处理完毕,把刚才new的那个requestAttributes请求标志置为完成
                // 上边判断请求是否存活isRequestActive(),就是跟这个有关
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

	protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
			@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {

		if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
            // 实际上RequestAttributes对象是一个ServletRequestAttributes对象
            // 绑定了request和response
			return new ServletRequestAttributes(request, response);
		}
		else {
			return null;  // preserve the pre-bound RequestAttributes instance
		}
	}

   // 把ServletRequestAttributes 设置到当前的ThreadLocal变量中
	private void initContextHolders(HttpServletRequest request,
			@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
	}

看到这,这个BeanDefinition对应的bean实例化出来了,那开篇说过,有@Scope注解的beanDefinition是被狸猫换太子的,是一个FactoryBean(ScopedProxyFactoryBean),还得继续看ScopedProxyFactoryBean.getObject()

 ScopedProxyFactoryBean实现了BeanFactoryAware,setBeanFactory方法执行的时候创建的代理对象,getObject返回代理对象。

怎么代理的就先不管了,单写一篇学习代理。代理对象的方法拦截器DynamicAdvisedInterceptor中会再getBean()从beanFactory获取最初的beanDefinition的对象进行invoke,最初的beanDefinition也是个scope类型,跟上边的ScopedProxyFactoryBean实例化走一个路子。

public class ScopedProxyFactoryBean extends ProxyConfig
		implements FactoryBean, BeanFactoryAware, AopInfrastructureBean {
   
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		pf.setTargetSource(this.scopedTargetSource);

		Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
		Class beanType = beanFactory.getType(this.targetBeanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
					"': Target type could not be determined at the time of proxy creation.");
		}
		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
		}

		// Add an introduction that implements only the methods on ScopedObject.
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

		// Add the AopInfrastructureBean marker to indicate that the scoped proxy
		// itself is not subject to auto-proxying! Only its target bean is.
		pf.addInterface(AopInfrastructureBean.class);

		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}

	@Override
	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;
	}

} 
  

 

到这基本就完事了,总结:

1、每次请求上来将新建一个ServletRequestAttribute对象绑定request和response,并注册到RequestContextHolder类中的ThreadLocal变量上

2、在Web容器refresh方法,ServletWebServerApplicationContext#postProcessBeanFactory中注册工具类SessionScope和RequestScope用来实例化bean时调用

3、解析beanDefinition时,@SessionScope或@RequestScope注解的beanDefinition的Scope属性设置为request或session,并且原先的beanDefinition已经被替换(ScopedProxyFactoryBean)

4、doGetBean获取Scope属性为request或session类型的bean类实例时使用SessionScope.get(beanName, ObjectFactory)或RequestScope.get(beanName, ObjectFactory)来获取,先获取这个ServletRequestAttribute对象,如果这对象中对应的HttpServletRequest或request对应的Session没有这个bean实例,再调用ObjectFactory.getObject()方法创建,getObject()具体工作是由AbstractBeanFactory#createBean来创建

5、上一步创建的东西只是ScopedProxyFactoryBean,FactoryBean.getObject() 获取代理对象,代理对象拦截器方法中再getBean()获取真正的bean对象执行操作

 

 

 

 

你可能感兴趣的:(笔记,Spring)