bean的作用域解析

打印 上一主题 下一主题

主题 905|帖子 905|积分 2715

说明
  意义
    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)包括自定义作用域
代码展示
  1. // mbd 指的是前面部分的 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  2. if (mbd.isSingleton()) {
  3.     sharedInstance = getSingleton(beanName, () -> {
  4.         try {
  5.             return createBean(beanName, mbd, args);
  6.         }
  7.         catch (BeansException ex) {
  8.             destroySingleton(beanName);
  9.             throw ex;
  10.         }
  11.     });
  12.     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  13. }
  14. else if (mbd.isPrototype()) {
  15.     // It's a prototype -> create a new instance.
  16.     Object prototypeInstance = null;
  17.     try {
  18.         beforePrototypeCreation(beanName);
  19.         prototypeInstance = createBean(beanName, mbd, args);
  20.     }
  21.     finally {
  22.         afterPrototypeCreation(beanName);
  23.     }
  24.     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  25. }
  26. else {
  27.     String scopeName = mbd.getScope();
  28.     // 这一步获取的就是存储单例的缓存,针对不同类型获取不同的缓存块【如request对应的RequestScope,session对应的SessionScope】
  29.     final Scope scope = this.scopes.get(scopeName);
  30.     if (scope == null) {
  31.         throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
  32.     }
  33.     try {
  34.         //类似于getSingleton的方式,在缓存中拿不到才会走工厂方法获取
  35.         Object scopedInstance = scope.get(beanName, () -> {
  36.             beforePrototypeCreation(beanName);
  37.             try {
  38.                 return createBean(beanName, mbd, args);
  39.             }
  40.             finally {
  41.                 afterPrototypeCreation(beanName);
  42.             }
  43.         });
  44.         bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
  45.     }
  46.     catch (IllegalStateException ex) {
  47.         throw new BeanCreationException(beanName,
  48.                 "Scope '" + scopeName + "' is not active for the current thread; consider " +
  49.                 "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
  50.                 ex);
  51.     }
  52. }
复制代码
 
 
代码分析
  对于Prototype部分的分析
    1.先是涉及到检测循环依赖部分的
        beforePrototypeCreation(beanName);  //记录循环依赖,针对还没有创建完成的Bean进行记录
        afterPrototypeCreation(beanName);  //销毁记录,已创建完了就必须销毁,不然A依赖于B,B都创建完了,你还觉得别人还没创建
      2.涉及创建Bean部分的

        了解过源码的都知道,在创建过程中,如果bean实例化但是未初始化会有一个对外暴露的方式,就是存储于单例池中
        故对于多例情况,bean是不做缓存的
 
  对于Singleton部分的分析
    对于单例的bean有它自己的处理逻辑,getSingleton方法:
  1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  2.     //加锁是保证单例创建的不冲突
  3.     synchronized (this.singletonObjects) {
  4.         //尝试从单例池中获取
  5.         Object singletonObject = this.singletonObjects.get(beanName);
  6.         if (singletonObject == null) {
  7.             //记录循环依赖,针对还没有创建完成的Bean进行记录
  8.             beforeSingletonCreation(beanName);
  9.             boolean newSingleton = false;
  10.             boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  11.             if (recordSuppressedExceptions) {
  12.                 this.suppressedExceptions = new LinkedHashSet<>();
  13.             }
  14.             try {
  15.                 //从工厂方法中,创建bean对象
  16.                 singletonObject = singletonFactory.getObject();
  17.                 newSingleton = true;
  18.             }
  19.             catch (IllegalStateException ex) {}
  20.             catch (BeanCreationException ex) {}
  21.             finally {
  22.                  //销毁记录,已创建完了就必须销毁
  23.                 afterSingletonCreation(beanName);
  24.             }
  25.             if (newSingleton) {
  26.                 //创建完了要添加进入单例池
  27.                 addSingleton(beanName, singletonObject);
  28.             }
  29.         }
  30.         return singletonObject;
  31.     }
  32. }
复制代码
 
  对于其余部分的分析(包括request,session等和自定义都是走这部分的逻辑)
    针对request,session等,代码  scope.get  这部分深入进去其实是通用方法(也是模板设计模式),AbstractRequestAttributesScope类#get方法:
  1. @Override
  2. public Object get(String name, ObjectFactory<?> objectFactory) {
  3.     RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
  4.     Object scopedObject = attributes.getAttribute(name, getScope());
  5.     if (scopedObject == null) {
  6.         scopedObject = objectFactory.getObject();
  7.         attributes.setAttribute(name, scopedObject, getScope());
  8.         // Retrieve object again, registering it for implicit session attribute updates.
  9.         // As a bonus, we also allow for potential decoration at the getAttribute level.
  10.         Object retrievedObject = attributes.getAttribute(name, getScope());
  11.         if (retrievedObject != null) {
  12.             // Only proceed with retrieved object if still present (the expected case).
  13.             // If it disappeared concurrently, we return our locally created instance.
  14.             scopedObject = retrievedObject;
  15.         }
  16.     }
  17.     return scopedObject;
  18. }
复制代码
 
    这块便是针对缓存的获取,通用理解为 attributes.getAttribute(name, getScope()); 等同于session.getAttribute(beanName)或 request.getAttribute(beanName)
    工厂方法( Lambda表达式部分)针对的便是缓存没有时候的创建逻辑
 
分析汇总
  1.对于作用域,本质上是存储在此类单例bean的高速缓存中,并且对该命名bean的所有后续请求和引用都返回该高速缓存的对象,便是为了达到以空间换时间的优化方式。
  2.对于创建Bean,都要进行循环依赖的预防。
 
  1. AbstractRequestAttributesScope
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表