说明

  意义

    1.在Spring中,Bean的作用域可以通过scope属性来指定。

       2.指定作用域的目的是 存储在此类单例bean的高速缓存中,并且对该命名bean的所有后续请求和引用都返回该高速缓存的对象。(本身的理念就是以空间换时间的思维,创建步骤繁杂,而且频繁用到,我就存起来,下次用的时候就不用了创建了)

       3.了解了目的之后,自然也就有了多种类型,大多数会使用singleton,当然也会有希望每次用到的就是新产生的故而出现prototype类型,还有就是某些范围经常用到,另一些范围不经常用到的,衍生了request和session的范围性质的单例

  类型与范围

    常见的有:

      1)singleton:代表单例的,也是默认值(singleton存储在三级缓存内,本质上是容器applicationcontext里面的三级缓存)

      2)prototype:代表多例的(prototype不会对bean进行存储,而是在每次需要的时候进行创建)

      3)request:代表范围性质的单例(request存储在对应的请求构建的请求对象里面setAttribute)

      4)session:代表范围性质的单例(session存储在对应的请求构建的请求对象里面setAttribute)

      5)application:application则是作用域整个应用里面多个applicationcontext共享

      6)包括自定义作用域

代码展示

// mbd 指的是前面部分的 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} 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);
} else {
String scopeName = mbd.getScope();
// 这一步获取的就是存储单例的缓存,针对不同类型获取不同的缓存块【如request对应的RequestScope,session对应的SessionScope】
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
//类似于getSingleton的方式,在缓存中拿不到才会走工厂方法获取
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);
}
}

代码分析

  对于Prototype部分的分析

    1.先是涉及到检测循环依赖部分的

        beforePrototypeCreation(beanName);  //记录循环依赖,针对还没有创建完成的Bean进行记录

        afterPrototypeCreation(beanName);  //销毁记录,已创建完了就必须销毁,不然A依赖于B,B都创建完了,你还觉得别人还没创建

      2.涉及创建Bean部分的

        了解过源码的都知道,在创建过程中,如果bean实例化但是未初始化会有一个对外暴露的方式,就是存储于单例池中

        故对于多例情况,bean是不做缓存的

  对于Singleton部分的分析

    对于单例的bean有它自己的处理逻辑,getSingleton方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
//加锁是保证单例创建的不冲突
synchronized (this.singletonObjects) {
//尝试从单例池中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//记录循环依赖,针对还没有创建完成的Bean进行记录
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//从工厂方法中,创建bean对象
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {}
catch (BeanCreationException ex) {}
finally {
//销毁记录,已创建完了就必须销毁
afterSingletonCreation(beanName);
}
if (newSingleton) {
//创建完了要添加进入单例池
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

  对于其余部分的分析(包括request,session等和自定义都是走这部分的逻辑)

    针对request,session等,代码  scope.get  这部分深入进去其实是通用方法(也是模板设计模式),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;
}

    这块便是针对缓存的获取,通用理解为 attributes.getAttribute(name, getScope()); 等同于session.getAttribute(beanName)或 request.getAttribute(beanName)

    工厂方法( Lambda表达式部分)针对的便是缓存没有时候的创建逻辑

分析汇总

  1.对于作用域,本质上是存储在此类单例bean的高速缓存中,并且对该命名bean的所有后续请求和引用都返回该高速缓存的对象,便是为了达到以空间换时间的优化方式。

  2.对于创建Bean,都要进行循环依赖的预防。

AbstractRequestAttributesScope

bean的作用域解析的更多相关文章

  1. 8 -- 深入使用Spring -- 2...2 指定Bean的作用域

    8.2.2 指定Bean的作用域 当使用XML 配置方式来配置Bean实例时,可以通过scope来指定Bean实例的作用域,没有指定scope属性的Bean实例作用域默认是singleton. 当采用 ...

  2. spring bean自动注入

    使用 @Repository.@Service.@Controller 和 @Component 将类标识为 Bean Spring 自 2.0 版本开始,陆续引入了一些注解用于简化 Spring 的 ...

  3. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  4. Spring中Bean的实例化

                                    Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...

  5. 基于注解的bean配置

    基于注解的bean配置,主要是进行applicationContext.xml配置.DAO层类注解.Service层类注解. 1.在applicationContext.xml文件中配置信息如下 &l ...

  6. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  7. Spring Bean详细讲解

    什么是Bean? Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象. Spring 容器会自动完成@bean对象的实例化. 创建应用对象之间的协作关系的行为称为:装配( ...

  8. 【解决方案】 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userHandler': Injection of resource dependencies failed;

    一个错误会浪费好多青春绳命 鉴于此,为了不让大家也走弯路,分享解决方案. [错误代码提示] StandardWrapper.Throwableorg.springframework.beans.fac ...

  9. 分享个 之前写好的 android 文件流缓存类,专门处理 ArrayList、bean。

    转载麻烦声明出处:http://www.cnblogs.com/linguanh/ 目录: 1,前序 2,作用 3,特点 4,代码 1,前序  在开发过程中,client 和 server 数据交流一 ...

随机推荐

  1. myeclipse添加subclipse插件支持subversion1.9

    为了安装subclipse插件,费了很多周折,本来我以为直接用eclipse marketplace搜索安装就行,可是由于网络原因,安装不了. 然后下载安装包吧.目前从国内网站上下载不了支持subve ...

  2. Solution -「HNOI」EVACUATE

    Sol. 可以发现人的移动除了不能穿墙以外没有别的限制.也就是说人的移动多半不是解题的突破口. 接下来会发现出口的限制很强,即出口每个时刻只能允许一个人出去. 每个时刻? 不难想到对于每一个时刻每一个 ...

  3. C++ 加速(卡常)技巧【超级 快读、快写】

    C++ \texttt{C++} C++ 加速技巧 快读快写 快读 inline int read() { int x = 0, w = 0; char ch = 0; while (!isdigit ...

  4. Github隐藏使用技巧(超详解)

    目录 github使用说明 查看别人的主页和项目 上传自己的项目 使用git下载github上的文件 使用git实现代码管理 使用git恢复被修改的文件 更多关于git使用小技巧 github使用说明 ...

  5. ES6中class方法及super关键字

    ES6 class中的一些问题 记录下class中的原型,实例,super之间的关系 //父类 class Dad { constructor(x, y) { this.x = 5; this.y = ...

  6. Odoo14 TypeError: Cannot read property 'classList' of undefined

    Traceback: TypeError: Cannot read property 'classList' of undefined at Class.setLocalState (http://l ...

  7. Java学习 (五)基础篇 数据类型

    目录 数据类型 强类型语言 弱类型语言 Java数据类型分为两大类 八大字节类型(重点) Java数据类型拓展 整数拓展:进制 浮点数拓展 字符类型 布尔值拓展 数据类型 强类型语言 要求变量的使用要 ...

  8. fijkplayer问题反馈:暂停时拖动进度光标,在窗口模式与全屏模式间切换后,进度光标不能及时更新、正常跟进

    fijkplayer-0.8.4很优秀,造福苍生,非常感谢! 使用fijkplayer-0.8.4开发的过程中遇到以下问题,特此记录.提交上传:https://github.com/befovy/fi ...

  9. LuoguP2254 [NOI2005]瑰丽华尔兹 (单调队列优化DP)(用记忆化过了。。。)

    记忆化 #include <cstdio> #include <iostream> #include <cstring> #include <algorith ...

  10. 使用Linux、Nginx和Github Actions托管部署ASP.NET Core 6.0应用

    使用Linux.Nginx和Github Actions托管部署ASP.NET Core 6.0应用 前言 本文主要参考微软这篇文档而来 Host ASP.NET Core on Linux with ...