前言

前两天分析了一波@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\requestString 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

 @Overrideprotected 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

@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();synchronized (mutex) {return super.get(name, objectFactory);}}// 进父类@Overridepublic 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<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<>("Request context");public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}
}

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

 @Overridepublic Object getAttribute(String name, int scope) {// scope是requestif (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

 @Overrideprotected 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 {// 忽略了无关代码// 获取原先的RequestAttributeRequestAttributes 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和responsereturn 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 ProxyConfigimplements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {@Overridepublic 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());}@Overridepublic 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注解@Scope---SessionScope和RequestScope相关推荐

  1. Spring注解@Scope

    Spring注解@Scope 一.@Scope注解 1.@Scope注解作用 @Scope注解用于设置实例的作用域. 默认值是单实例,即当IOC容器启动后就调用该方法创建对象放到IOC容器中,以后每次 ...

  2. 一分钟学会spring注解之@Scope注解

    今天主要从以下几方面来介绍一下@Scope注解 @Scope注解是什么 @Scope注解怎么使用 @Scope注解的使用场景 1,@Scope注解是什么 @Scope注解是springIoc容器中的一 ...

  3. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  4. 浅谈Spring注解

    Spring目前的趋势是使用注解结合Java代码而不是配置来定义行为.属性.功能.规则和扩展点,因此梳理注解也是梳理Spring功能点的很好的方式,全面的梳理可以补足我们知识点的漏洞. 查找所有注解 ...

  5. java spring注解教程,spring注解

    spring注解 扫描指定的包中的类上的注解,常用的注解有: @Controller 声明Action组件 @Service    声明Service组件    @Service("myMo ...

  6. java spring框架 注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

  7. spring中间scope详细解释

    0.思维导图 1. scope概论 spring中scope是一个很关键的概念.简单说就是对象在spring容器(IOC容器)中的生命周期,也能够理解为对象在spring容器中的创建方式. 2. sc ...

  8. Spring注解Annotation

    1. 使用Spring注解来注入属性  1.1. 使用注解以前我们是怎样注入属性的  类的实现: Java代码  public class UserManagerImpl implements Use ...

  9. 注解赋值可以是方法_P7笔记,把Spring注解讲的明明白白

    关注公众号领取海量架构师资料 环境搭建 注解的方式是通过配置类的方式来注入组件,注解注入要比XML注入的方式简单,注解注入也需要在前者的基础上,添加一个spring-context的包,也是实际开发中 ...

最新文章

  1. Eclipse编辑jsp、js文件时,经常出现卡死现象解决汇总
  2. Qt Creator使用灯光
  3. mysql笔记 doc_Mysql DOC阅读笔记
  4. 【转】C++ win32窗口创建详解
  5. 如此沙雕的代码注释,还是程序员会玩!
  6. Python绘图,圣诞树,花,爱心 | Turtle篇
  7. python3数字类型_python3笔记八:python数据类型-Number数字
  8. python无法使用decode,Python处理文件名或路径名中无法decode代码问题
  9. SQLserver nText和varchar 不兼容
  10. 手工matlab下K-means聚类算法实现而不是调用库函数
  11. 日本将推出“隐形列车”,你要去体验一番吗?
  12. rtx服务器插件的作用,腾讯通RTX手机版插件介绍 - 有度帮助中心
  13. Receptive Field Block Net for Accurate and Fast Object Detection
  14. 2022年的电视评判标准,为何还要强调画质?
  15. [附源码]java毕业设计大学生兼职招聘网站
  16. 物联网解决方案架构及其流程
  17. 正面硬刚Beats!这款耳机从美国红回中国,细腻音质千元内无敌手!
  18. C++11 boost::spirit::qi简单的XML解析器示例
  19. jstl标签c:choose,c:when,c:otherwise用法
  20. Mob短信验证码的继承

热门文章

  1. 御坂美琴(入门bfs+STL)
  2. VS 2017 添加 ReportViewer
  3. android:scaleType属性说明
  4. 开发随笔-写支付宝支付成功接受通知
  5. 分布式高级数据库(怀念一下我得了及格的高级数据库)
  6. C语言:static作用(修饰函数、局部变量、全局变量)
  7. Linux学习——总结ARM裸机开发步骤
  8. 缓冲区溢出攻击与防范
  9. 为什么学习python?(知乎大神的回答)
  10. ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析