說明
意義
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/506011.html
標籤:其他
上一篇:Python影像處理丨基于K-Means聚類的影像區域分割
下一篇:day34-IO流01
