1. Spring源碼9. refreshContext()刷新應用上下文
上一篇 prepareContext()准備應用上下文 中分析了spring容器的准備, 共計執行了如下8步:
准備刷新, 執行了兩步:
清空CachingMetadataReaderFactory中的緩存
設置刷新開始事件, 設置closed為false, active為true, 標記容器處於active狀態
AbstractApplicationContext中定義了模板方法, refreshBeanFactory和getBeanFactory調用的是GenericApplicationContext中實現的方法
更新this.refreshed欄位為true, 表示已經更新了, 然後beanFactory設置serializationId, 最後返回beanFactory
beanFactory是GenericApplicationContext中DefaultListableBeanFactory類型的成員變數, 設置beanFactory, 一共執行了
後續處理各個beanFactory, 當前applicationContext是的實例, postProcessBeanFactory執行了三步
進行了兩個操作, 首先添加了一個的Aware Bean處理器, ServletContextAware的子類Bean在實例化過程中, 會被注入servletContext和servletConfig對象, 然後beanFactory中注冊了request和session兩個scopes, 注冊了幾個Autowired依賴類
注冊了request, session兩個scope, 然後注冊ServletRequest, ServletResponse, HttpSession, WebRequest
BeanFactoryPostProcessor是一個介面, 處理beanFactory中所有的bean, 在所有的beanDefinition載入完成之後, BeanFactoryPostProcessor可以對beanDefinition進行屬性的修改, 之後再進行bean實例化
是BeanFactoryPostProcessor的子介面, 定義了方法, 會在postProcessBeanFactory方法執行之前, 獲取bean定義, 並注冊到spring容器中
如果beanFactory是BeanDefinitionRegistry的子類, 按優先順序處理類型的後置處理器, 最後處理傳入的其他類型後置處理器, 處理流程如下:
如果beanFactory不是BeanDefinitionRegistry的子類, 那麼直接遍歷傳入的傳入的beanFactoryPostProcessors, 調用元素的postProcessBeanFactory方法
最後處理beanFactory中注冊的其他類型的BeanFactoryPostProcessor, 獲取bean名稱, 維護到postProcessorNames列表中, 之後的處理步驟如下:
處理了@Configuration註解, 掃描項目中的BeanDefinition, 這篇文章 詳細剖析了的源碼
BeanPostProcessor是一個介面, Bean後置處理器, 在bean實例化, 之前執行方法, 在bean實例化之後執行方法, 實現了對bean實例的增強
beanFactory中獲取BeanPostProcessor類型的bean名稱, 維護到postProcessorNames數組中, 將BeanPostProcessor列表分為四類:
beanFactory先添加一個BeanPostProcessorChecker類型的BeanPostProcessor, 然後在將各類PostProcessors列表排序, 分別添加到beanFactory的beanPostProcessor列表中, 最後再添加一個ApplicationListenerDetector
先判斷容器beanFactory中是否包含messageSource bean定義, 存在的話, 直接獲取bean, 如果不存在的話, 那麼手工注冊一個messageSource單例bean, 然後賦值給this.messageSource
先判斷容器beanFactory中是否有applicationEventMulticaster bean定義, 存在的話, 獲取bean實例, 不存在的話, 實例化一個, 手工注冊一個單例bean, 然後賦值給this.applicationEventMulticaster
AbstractApplicationContext沒有實現該方法, 用於通知子類刷新容器
調用父類GenericWebApplicationContext#onRefresh方法, 然後創建webServer, 之後調用父類GenericWebApplicationContext#initPropertySources方法, 將servletContext維護到environment的servletContextInitParams屬性中
初始化主題, 可以讓頁面顯示不同的樣式
首先將硬編碼的ApplicationListener先添加this.applicationEventMulticaster.defaultRetriever.applicationListeners中, 然後將注入的listener bean維護到this.applicationEventMulticaster.defaultRetriever.applicationListenerBeans, 最後處理earlyEvent
beanFactory.preInstantiateSingletons() 源碼剖析
什麼都沒有做
列印@Condition註解評估日誌
2. 【原創】react-源碼解析 - forward-ref&context(4)
通常我們會通過ref去獲取Dom節點的實例,或者ClassComponent的實例,但是,如果我么們的組件是一個function類型的component,由於functionComponent是沒有實例的所以我們在使用的時候也相應的取不到改組件的this,當然ref也一樣。這時react為我們提供了一個forwardRef方法:
通過這種方式創建的函數類型的組件,使我們能夠在函數中繼續使用ref屬性,當然我們在實際的應用的中也不會傻到去取函數類型組件的ref,因為我們知道它是沒有實例的。但是,當我們在使用其他庫提供的組件的時候,我們可能並不知道這個這個組件的類型,這時如果能夠合理的使用這個方法將會為我們省去不必要的麻煩,同時這里也有HOC的思想在裡面。接收一個組件,返回對原組件進行包裝的新的組件。接下來我們去看看方法的源碼: forwardRef 源碼很簡單,改方法返回了一個Oject具有render屬性,同時$$typeof為"react.forward_ref"的Symbol值。
這里可能存在對於type屬性的概念混淆。我們一定不能認為使用forward創建出的組件的$$typeof屬性為:'react.forward_ref'。我們使用forwardRef創建的組建的額時候,實際是將上面例子中的TargetCom作為參數傳入到CreateElement方法中的,實際返回的element中的$$typeof還是REACT_ELEMENT_TYPE, 也就是說這里我們將TargetCom{創建出的對象--具有render和$$typeof屬性}傳入,其中CreateElement的type屬性為forward方法返回的那個對象,也就是說在type對象裡面有個叫做$$typeof的屬性這個屬性的鍵值為:'react.forward_ref',
在後安的渲染過程中有很多判斷,其中有一些就是更具$$typeof展開的,這里我們一定要搞清楚凡是通過CreateElement創建的組件的$$typeof屬性都為: 'REACT_ELEMENT_TYPE'。
這里我們還是按照慣例對api進行一下簡單的說明,我們知道在react中是通過props屬性來實現組件間通信的,這種通信方式存在的問題在於,雖然父子組件之間通信很方便但是當我們的組件嵌套層級很深,這時候如果使用props傳參就不太現實了,首先中間層的組件不一定是你自己寫的其次中間層組件聲明的props對於這些組件本身沒有任何意義,這個時候我們就需要使用context方法幫助我們實現多級組件之間的通信。我們在頂層組件中提供了context對象之後,所有的後代組件都可以訪問這個對象。以此達到跨越多層組件傳遞參數的功能。在react當前版本中有兩種實現context的方式:
(1)ParentComponent.childContextTypes{} == {不推薦,下個大版本會廢棄}
(2)const { Provider, Consumer } = React.createContext('default');
在使用childContextTypes時候我們需要在父級組件中聲明一個getChildContext的方法,該方法返回一個對象,這個對象就是我們需要傳給後代組件的context對象。當我們在使用第一種方法的時候我們需要在組件上聲明context對象中屬性的類型,有些類似於react的PropTypes類型檢測。同時需要在使用到context的後代組件中聲明contextTypes類似於下面這種寫法:
如果不這樣聲明的話,在後代組價中是取不到context對象的。這里我們需要注意的是我們在子組件中使用context的時候,需要哪個屬性就必須去contextTypes中聲明,因為改組件的上級組件不止一個上級組件中的context也不止一個。而createContext方法的使用就簡化了很多,首先我們看到改方法返回兩個對象Provider, Consumer分別為context的提供方和訂閱方:
在上層組件中聲明之後,在想用到context的後代組件中國使用Consumer包括起來就可以訪問到之前聲明的context: ReactContext
從源碼中我們可以看到CreateContext方法創建了一個對象改對象有一個_currenValue屬性記錄context的變化,這個對象Provider屬性中聲明context,然後使改對象的Consumer屬性指向對象本身,我們在使用Consumer的時候就直接從context的currenValue上去取值。以上就是react中的Createcontext方法的實現原理,當然實際過程並沒有這么簡單,至於具體的實現我們接著往下看。同時這里我們也需要注意該對象下的$$typeof屬性並不是用來替換ReactElement中的$$typeof, 與我們之前將到的forwardRef中聲明的$$typeof一樣都只是我們傳入CreateElement方法中type屬性上的內容。
了解更多: react-source-code
3. Spring源碼6:createApplicationContext()實例
上一篇 printBanner()列印Banner 中非了springboot如何列印Banner
在 初始化SpringApplication實例 中, 已經分析了當前模塊web類型為SERVLET, 所以當前實例化了一個對象
由上面類圖,我們可以看出, 類繼承關系如下:
用來載入Resource, 初始化的過程中, 實例化了ClassLoader,
抽象ApplicationContext, 定義了ApplicationContext一些模板方法, 在實例化的過程中, 調用了getResourcePatternResolver()方法, 構造了一個, 規定了如何查找資源, 例如從classpath, 根路徑, 從war包等查找資源
初始化了一個DefaultListableBeanFactory
由上面類圖, 我們可以看出DefaultListableBeanFactory的繼承關系:
提供了bean別名的增刪改查功能
默認的單例Bean注冊器, 提供了單例bean增刪改查等功能
提供了FactoryBean的增刪改查方法
抽象BeanFactory, 定義了通用的beanFactory的模板方法, 添加了對beanFactory對Scope的支持, scope主要有五種, singleton, prototype, request, session和application,
抽象自動配置BeanFactory, 實現了創建Bean, 實例化Bean, 欄位配置Bean, 自動裝配依賴Bean的方法
BeanFactory默認實現, spring IOC默認容器類
重寫了AbstractApplicationContext的getResourcePatternResolver()方法, 返回一個對象, 構造函數中顯示調用父類GenericApplicationContext的構造函數
隱式調用父類GenericWebApplicationContext構造函數, 什麼都沒有做
首先, 初始化一個AnnotatedBeanDefinitionReader, 然後再實例化一個對象
用於讀取和解析bean定義
註解bean名稱生成器, 用於生成Bean名稱
Scope註解的解析器, 解析出Scope的模式ScopedProxyMode, 以及Scope的名稱
@Conditional註解的條件評估器, 評估是否滿足條件
注冊bean處理器, bean名稱需要加上org.springframework.context.annotation前綴:
如果basePackages不為空的話, 掃描basePackages中定義的bean, 當前應用中沒有配置basePackages, 所以不會去掃描bean
實例化過程中, 會先調用父類GenericApplicationContext構造函數, 實例化了一個DefaultListableBeanFactory, 作為Spring IOC容器, 的構造函數實例化了AnnotatedBeanDefinitionReader對象, 用於讀取Spring的bean定義, 在實例化AnnotatedBeanDefinitionReader的過程中, 注冊了幾個bean, 用來處理相應的註解
我們將會在下一篇 SpringBootExceptionReporter異常上報 , 繼續閱讀springboot源碼
4. Netty源碼_ChannelPipeline和ChannelHandlerContext詳解
ChannelPipeline 相當於 ChannelHandler 的集合,用於處理或攔截 Channel 的入站事件和出站操作。
這里就有兩個問題:
ChannelPipeline 管理 ChannelHandler 集合,是利用 ChannelHandler 創建一個上下文對象 ChannelHandlerContext , 而 ChannelPipeline 存儲的就是這個上下文對象。
這個 ChannelHandlerContext 對象下一節將重點講解。
ChannelPipeline 管理的 ChannelHandler 集合處理 I/O 事件的流程圖如下:
可以看出 ChannelPipeline 將管理的 ChannelHandler 分為兩種:
不要看 ChannelPipeline 方法很多,其實主要分為兩類:
你會發現每個添加方法都有多一個 EventExecutorGroup 參數的對應方法,它作用是什麼呢?
在 ChannelPipeline 實現中:
你會發現 ChannelPipeline 的主要功能就兩個:
上下文 ChannelHandlerContext 介面與 ChannelPipeline 聯系非常大
ChannelHandlerContext 介面的重要作用:
ChannelHandlerContext 的功能大概分為兩類:
5. Gin 的啟動過程、路由及上下文源碼解讀
Engine 是 gin 框架的一個實例,它包含了多路復用器、中間件和配置中心。
gin 通過 Engine.Run(addr ...string) 來啟動服務,最終調用的是 http.ListenAndServe(address, engine) ,其中第二個參數應當是一個 Handler 介面的實現,即 engine 實現了此介面:
Engine.ServeHTTP() 會先初始化一個空的上下文,然後掛上請求 c.Reuqest = req ,隨後執行 engine.handlerHTTPRequest(c) (包含主要處理邏輯的函數)。
以上就是正常處理一個請求的主要邏輯,其他的就現階段來說先忽略了。
Engine 組合了 RouterGroup。
RouterGroup 實現了 IRouter 介面,IRouter 介面是 IRoutes 介面和 Group 函數組合而成。
RouterGroup 的結構體只有四個屬性:
當新建 Engine 時,會初始化一個 RouterGroup 結構,RouterGroup 是組合在 Engine 中的(所以 Engine 可以調用 RouterGroup 的所有方法),同時 Engine 的引用也記錄在了 RouterGroup 上。
如上,RouterGroup 實現了 IRouter 介面,下面是一些方法的實現。
gin 通過上方 RouterGroup 暴露的幾個方法添加路由,底層使用的方法是 Engine.addRoute(method, path string, handlers HandlerChain) 。
Engine.trees 屬性是存儲所有路由信息的總入口。它是一個切片,其中每個元素對應一種 method 並且是一個多叉樹的根節點。
當 addRoute 時,先根據 method 找到對應的 tree (Engine.trees[i])。然後會比較 加入者 的 path 和 node 中的 path 相似的部分, 相似的部分 作為 父結點,不同的部分作為 子結點。以 多叉樹 的方式存儲下來。
這里會把 URL 中的路由變數也當作字元串存入樹中,因為相同 URL 他們的變數也是一樣的。
當請求進來時,因為 Engine 實現了 Handler 介面,所以最後會調用到 Engine.ServeHTTP 內。
找路徑在
root.getValue() 比較復雜,這里就不多解釋了。
[email protected] context.go
Context 中定義了一些屬性和方法,用於擴展一些功能。
可以看到,這些方法主要用來獲取 gin 自身 Context 的一些信息。
Context 中保存了所有 handlers 列表,存在 Context.handlers 數組中,並用下標 Context.index 標記當前執行的位置。
當主動取消調用鏈時,會將 index 設置成一個最大值 63( math.MaxInt8 / 2 ),也即調用鏈最大支持 64 個函數。
Context 中還提供了其他一些函數,當取消調用鏈的時候,可以設置請求返回的狀態碼和返回數據信息等。
Context 中的 httpWriter 整理一下。
gin 在 Context 中定義了錯誤信息欄位 Context.Errors 切片,可以鏈式存儲錯誤信息。
Go 原生的 Context 是通過 ValueContext 來存儲元數據信息的,每個 ValueContext 只能存儲一對信息,存儲多個信息對需要將許多 ValueContext 組成鏈條,讀寫很不高效。
gin 的 Context 中存的元數據數據是存在 Context.Keys map[string]interface{} 屬性中的,比起原生的 Context 使用起來會更高效。
是指用在 URL 路徑中設置的參數,如 /user/:id 的 id 參數。
存儲在 Context.Params 屬性中,其本質是一個切片,每一個元素是一個 K/V 元組。
因此,在 URL 中是可以使用重復的變數名的(如 /test/:id/case/:id ),但獲取值就需要自己從屬性中獲取了(如: c.Params[0] )。
Query 類是用在 URL 後的參數部分(如: ?id=1 )。
gin 通過 Context.queryCache 屬性存儲 query 參數,在調用獲取 Query 參數時以懶載入的方式初始化: c.queryCache = c.Request.URL.Query() 。
需要注意的是它也支持傳入 map 和 array,map 的傳入需要像這樣 ?m[k1]=v1&m[k2]=v2 ,array 的傳入像這樣 ?a=1&a=2 。
包含 PostForm、FormFile、MultipartForm 等。
先略
gin 為方便使用,通過綁定引擎設置了自動綁定用戶輸入和結構數據的方法。
這里包含設置狀態碼、設置響應頭以及等信息。
只說一些值得注意的
這些方法除了 .Value() 方法外,其他都是返回的默認空值,略。
6. 源碼分析->一個應用到底有幾個Context
相信很多人都知道是這樣計算的,那到底為什麼是這樣呢?
源碼分析基於android28源碼
什麼是Context呢?可以理解為上下文、運行環境,當需要獲取資源、系統服務以及啟動Activity、Service用到,也可以通過它跟系統交互。
通過以下繼承關系可以看出,Activity是繼承ContextWrapper
ContextWrapper內部有一個Context類型的成員變數mBase
mBase是通過attachBaseContext()方法賦值
是創建Activity的關鍵,
主要工作
(1)createBaseContextForActivity()內部實例化ContextImpl 對象;
(2)mInstrumentation.newActivity()內部通過反射實例化Activity對象;
(3)activity.attach()內部會調用attachBaseContext()方法給mBase對象賦值;
通過以下繼承關系可以看出,Application是繼承ContextWrappe
是創建Application的關鍵,
主要工作:
(1)ContextImpl.createAppContext()實例化ContextImpl ;
(2)mActivityThread.mInstrumentation.newApplication(),內部通過反射實例化Application,並把appContext傳遞過去,通過attach()方法給mBase賦值;
跟Activity類似就不再做分析。
經過分析發現:
1.每個Activity,Service,Application都有一個ContextImpl 類型的成員變數mBase,ContextImpl是Context的實現類。
2.細心的讀者可能發現,Activity,Service,Application都是繼承Context,其實他們本身是一個Context,也都實現了Context的抽象方法,
那麼一個Activity是否就擁有兩個Context呢?
是不是
這樣計算比較合適呢?
下面看下Context中常用的三個方法,
ContextImpl繼承Context,並實現了這三個方法,
Activity間接繼承Context,主要是在ContextWrapper實現了以上三個方法,從源碼中可以看出,最終還是調用了ContextImpl的實現。
下圖可以看出這幾個的關系,ContextWrapper顧名思義就是Context的包裝類(有ContextImpl的成員變數),並且實現了Context,這是一種裝飾者設計模式。當在Activity中調用getAsset()時,其實最終是調用mBase的getAsset()。
Activity間接繼承了Context,是為了擁有跟ContextImpl一樣的功能,但真正起作用的是mBase這個成員變數,所以一個Activity其實就只有一個Context起作用,那就是ContextImpl類型的mBase。
這種計算方法應該是沒有問題呢。
或許有人有這樣的疑問,一個應用不是只有一個Application嗎,為什麼計算公式是加上Application個數?單進程應用來說,一個應用確實只有一個Application,而多進程應用,那麼一個應用就有多個Application,所以應該說一個應用有一個或多個Application,一個進程有一個Application。
另外其他關於Context的常見面試題
1.Activity的this跟getBaseContext區別。
前者是Activity對象本身,後者是通過attachBaseContext傳入的ContextImpl對象mBase,兩者功能是一樣的,通過this最終還是會調到mBase對象中。
2.getApplication和geApplicationContext區別。
兩者都是返回Application對象,前者是Activity和Service裡面的方法,後者是Context中定義的方法。
3.應用組件的構造,onCreate、attachBaseContext的執行順序。
先是組件構造化,接著attachBaseContext,傳入ContextImpl對象,最後是onCreate方法。
4.談談你對Context的理解
先是Context的作用,然後是有幾種Context,Application、Service、Activity的Context有什麼區別以及繼承關系,
最後是mBase變數是如何實例化的。
以上分析有不對的地方,請指出,互相學習,謝謝哦!
7. Application中的 Context 和 Activity 中的Context區別
Context在我們開發中經常用到,不管是Framework提供給我們的四大組件,還是應用級別的Application,還是負責表現層的View相關類,甚至連我們很多時候創建的實體類都會需要持有一個Context的引用。那麼Context到底是什麼呢?
建議看這個: https://www.jianshu.com/p/b68de4c95b05
Context英文釋義是當前上下文,或者當前場景上,
官方文檔:Context
public abstractclass Context extends Object
Interface to globalinformation about an application environment. This is an abstract class whoseimplementation is provided by the Android system. It allows access toapplication-specific resources and classes, as well as up-calls forapplication-level operations such as launching activities, broadcasting andreceiving intents, etc.
由官方文檔,我們可以知道:
1.該類是一個 抽象(abstract class)類 ;
2.它描述的是一個應用程序環境的信息,即上下文;
3.通過它(Context)我們可以獲取應用程序的資源和類,也包括一些應用級別的操作(例如,啟動 Activity,廣播和服務等);
前面我們講過 Context 是一個抽象類,通過 Context我們可以獲取應用程序的資源和類,調用它們的方法,那麼具體定義的方法有哪些呢?我們來看一下 Context 的源碼:
源碼里的方法太多了,總共 4710 行。我們從以上部分源碼看到了熟悉的對象---Application、Activity、Service、Broadcast、這些對象和 Context 的關繫到底是什麼呢?我們看一下官方文檔可知:
1. Acitiivity 繼承自ContextThemeWrapper--->再繼承ContextWrapper--->Context。
2. Appliction 、Service繼承自ContextWrapper--->再繼承Context。
3. Application、Service 和 Activity 最終都是繼承自Context,所以它們是同一個上下文。
通過以上的繼承關系,我們就可以知道,Context的具體作用會包括:
- 啟動一個新的Activity
- 啟動和停止Service
- 發送廣播消息(Intent)
- 注冊廣播消息(Intent)接收者
- 可以訪問APK中各種資源,如Resources和AssetManager
- 創建View
- 訪問Package的相關信息
- APK的各種許可權管理
由上面分析的繼承關系,我們可以知道,Context創建的時機有三個:
①創建Application 對象時, 而且整個App共一個Application對象;
②創建Service對象時;
③創建Activity對象時;
所以應用程序App共有的Context數目公式為:
Service個數 + Activity個數 + 1(Application對應的Context實例)
如上,Android中context可以作很多操作,但是最主要的功能是載入和訪問資源。在android中常用的context有兩種,一種是application context,一種是activity context,通常我們在各種類和方法間傳遞的是activity context。
兩者的區別:
this 是Activity 的實例,擴展了Context,其生命周期是Activity 創建到銷毀。getApplicationContext()返回應用的上下文,生命周期是整個應用,應用摧毀它才被摧毀。Activity.this的context 返回當前activity的上下文,屬於activity ,activity摧毀時被摧毀。
使用Context時最需要注意的一個點就是,使用了不正確的context,比如有一個全局的數據操作類用到了context,這個時候就要getApplicationContext 而不是用ACtivity,如果在這個全局操作中引用的是Activity的context,那麼就會一直引用Activity的資源,導致GC無法回收這部分內存,從而最終導致了 內存泄漏 。
內存泄漏是開發中常見的錯誤之一,能不能發現取決於開發者的經驗,當然了我們也會依賴現有的內存泄漏庫,但是如果我們在開發的源頭減少內存泄漏的概率,那麼後期的工作會少很多。
以下是避免context相關的內存泄露,給出的幾點建議:
以下的表列舉的是三種Context對象的對應使用場景:
從表中可以看到,和UI相關的都使用Activity的Context對象。
小結:如上分析,Context在對應開發里的來源就是三個——Activity、Service和Appliaction,那麼我們該如何選擇使用哪一個Context對象呢?一個比較簡單的方法是,當你無法確定使用某個Context對象是否會造成長引用導致內存泄漏時,那麼就使用Appliaction的Context對象,因為Appliaction存在於整個應用的生命周期內。
在實際開發中,我們往往會為項目定義一個Applictaion,然後在AndroidMainfest.xml文件中進行注冊,
而且在自定義Application往往會定義好一個靜態方法,用以全局獲取application實例:
Activity和Application都是Context的子類,但是他們維護的生命周期不一樣。前者維護一個Acitivity的生命周期,所以其對應的Context也只能訪問該activity內的各種資源。後者則是維護一個Application的生命周期。
1.如何判斷context是屬於哪個activity?
2.全局不同如何獲取對應的context?
靜態載入一個Fragment,在onCreateView()方法中通過getActivity獲取上下文實例:
3.四大組件可以像普通Java類一樣,採用new的方式實例化嗎?
Android程序不像Java程序一樣,隨便創建一個類,寫個main()方法就能運行,Android應用模型是基於組件的應用設計模式,組件的運行要有一個完整的Android工程環境,在這個環境下,Activity、Service等系統組件才能夠正常工作,而這些組件並不能採用普通的Java對象創建方式,new一下就能創建實例了,而是要有它們各自的上下文環境,也就是我們這里討論的Context。可以這樣講,Context是維持Android程序中各組件能夠正常工作的一個核心功能類。
8. Spring源碼分析(一) XmlWebApplicationContext
spring是大家都會用的ioc框架,但是要真的了解spring還是需要好好研究一下才行,為此看了一些spring源碼,所以開始寫spring源碼分析的文章,這個是第一篇,先從ioc容器的啟動開始。
我們都知道,spring的ioc容器的最基本的介面就是BeanFactory,而ApplicationContext是包含了BeanFactory的所有信息,所以ioc容器在啟動的時候就是從AbstractApplicationContext的refresh方法開始的
具體的啟動流程就不說了,主要是這里有一個onRefresh方法,我們來看這個類,在這個類中覆寫了onRefresh方法
這是什麼東西?別急,我們來看看themeSource是什麼。
還是不太明白?那我們來看看的結構
原來ThemeSource是一個介面,而則實現了這個介面,在onRefresh把自己傳進去了,好吧,這塊就先看到這里。
我們直接到XmlWebApplicationContext這個類里,我們發現類有一個方法loadBeanDefinitions,而XmlWebApplicationContext覆寫了這個方法,我們來看看XmlWebApplicationContext是怎麼實現的
這里我們要介紹ioc容器里的一個介面BeanDefinitionReader,而XmlBeanDefinitionReader是BeanDefinitionReader的一個實現類,負責對xml的配置文件進行讀取,並放到ioc容器中。當讀取完配置文件後,通過loadBeanDefinitions方法將bean注冊到ioc容器中。
至此,ioc容器就啟動完成。
XmlWebApplicationContext的分析就到這里了。
9. Context是什麼
Context
Context,翻譯是上下文環境,在android應用開發中的地位舉足輕重,甚至源碼也有一句話:everything needs a context(看到過,但是忘了在哪裡了)。
從這個類圖中我們看到,Context是一個抽象類,一邊是Context的具體實現ContextImpl,一邊則是組件的封裝類ContextWrapper,便於二者各司其職,我理解是介面和實現分開,這樣比較符合開閉原則吧。
既然要搞清楚Context是幹嘛的,得先看Context的實例什麼時候創建的。這里有兩個我們經常接觸的組件,Activity和Service,以及用的較少的Application,它們的關系如下:
1.Application:保存應用的全局信息的類,它的實例在應用創建的時候被創建,生命周期最長,應用銷毀了才會被銷毀。
2.Activity:創建一個Activity便對應一個Context的實例,生命周期隨著Activity的周期變化。
3.Service:創建一個Service便對應一個Context的實例,生命周期隨著Service的周期變化。
下面逐一看看它們是什麼時候實例Context類的,我們需要知道,一個ContextWrapper類對應一個ContextImpl類的實例,因為具體實現的地方還是在ContextImpl中,ContextWrapper中的mBase變數指向ContextImpl變數的實例。
(1) Application的Context創建:
如圖:
跳過Process創建過程,在我們的應用被zygote進程孵化出來後,被反射調用main函數,這里會創建ActivityThread類的實例,該類並不是一個線程類的子類,但是它的main函數跑在新創建的進程的主線程。 創建後,調用attach方法,與AMS綁定:
這里傳入的mAppThread變數為ApplicationThread類,它為ActivityThread的內部類,作用就是作為應用端的binder服務端,負責接收AMS的調度,這里我們傳入這個服務端的句柄,讓AMS持有,從而AMS可以通過這個句柄來操控應用的行為。我們向AMS請求,則是通過AMS的句柄。接下來就到了AMS的邏輯:
在ApplicationThread的實現端中,就是把跨進程傳遞過來的各種數據再用一個AppBindData類保存下來,然後調用Handler對象H發送消息BIND_APPLICATION,最後在app線程中處理此邏輯,讓AMS線程可以不必同步阻塞。接下來就到了handleBindApplication:
Instrumentation類是用於管理Acitivty的工具類。這又有一個比較重要的對象出現,LoadedApk,它裡面保存了類載入器ClassLoader,data.info的對象便是它,makeApplication函數中的邏輯:
這里我們看到了ContextImpl的創建時機,就是在Application實例創建的時候:
如此就完成了Application的創建,並且調用attach方法把Application的mBase對象賦值給創建的ContextImpl,至此Application的創建就完成了,getApplicationContext() 返回的便是此處我們創建的Application類的對象。
這里為什麼要去LoadedApk類中利用類載入器,把對象創建出來呢?因為我們的Application類是可以自己拓展的,創建的時候是不確定應用自己是否復寫了Application對象, 利用類載入器就可以動態決定創建什麼類的對象了 ,我們只需要從PMS中獲取到application的具體類名即可,這個類名是寫在Mainfest文件中的,後面Activity也是利用這種機制去創建對象。
(2)Actvity的啟動過程,網上非常多文章分析了,我也就不再重復羅列出來了,大概的過程就是startActivity()被調用,然後檢查目標Acitivity的進程是否創建,沒創建就先向Zygote進程請求fork應用進程,然後一系列初始化過程後,最後在AMS模塊中的ActivityStackSupervisor中realStartActivityLocked()調用IApplicationThread代理類,調用到scheleLaunchActivity(),然後在應用的線程開始執行handleLaunchActivity(),最主要的邏輯在performLaunchActivity:
至於Activity的生命周期後面怎麼走的,這里不在乎,我們只看Context實例化過程。同樣的,這里也會創建ContextImpl對象,在Activity對象創建後,調用Attach(),這個函數挺重要的,我們看看都幹了什麼:
在attach函數中,也會為ContextWrapper中的mBase對象賦值一個ContextImpl對象,我們這里也能猜想到Service的創建估計也會有這個過程,而事實上Service的創建差不多也是這個過程,所以不再贅述了。
我們可以知道Context例的實例化都是在我們在Application,Activity和Service創建的時候會實例化,而這些組件的父類都是ContextWrapper,所以在創建的時候還會先創建一個ContextImpl類對象,然後給自己的父類mBase變數賦值,既然如此,Context的引用對應的就是Application,Activity和Service了,Context的用處就是提供了一組抽象的函數,讓子類去相對應的實現,當你持有一個Context引用的時候,你可以通過這個引用去獲取相對應組件的信息。比如持有一個Activity的Context引用,你可以在別的地方調用startActivity()去啟動一個新的activity。
總結:
1.Context是抽象類,所以實例化要在子類中,Application,Service,Activity是我們實例化的地方,一般應用創建會實例化一個Application類,Service和Activity則是在startService和StartActivity被調用的時候會實例化,它們都會創建一個ContextImpl類實例,給自己的父類ContextWrapper的mBase變數賦值。
2.Context是組件中通用操作的一組集合,當具體的子類實例化後,可以在別的地方通過保存一個Context引用去獲取信息和通用操作等,也就是說,我們可以通過這個引用去獲取到這個應用中的重要信息以及這個應用的通用操作。