一般來說我們在編程中,對象定義都是預先定義好的。一些 OOP 語言(包括 Python/Java)允許對象是 自省的(也稱爲 反射)。即,自省對象能夠描述自己:實例屬於哪個類?類有哪些祖先?對象可以用哪些方法和屬性?自省讓處理對象的函數或方法根據傳遞給函數或方法的對象類型來做決定。即允許對象在運行時動態改變方法成員等屬性。
得益於OpenERP ORM 模型的精巧設計,實際上 OpenERP 運行時也是動態讀取模塊信息並動態構建對象的。如在模塊開發中,繼承了 ‘res.users’, 新增一個方法或新增一個字段。在OpenERP 導入該模塊後, OpenERP 會馬上重構 ‘res.users’ 對象並將新增的方法或字段添加到該對象。那麼,OpenERP 是如何做到這點的呢? 讓我們從OpenERP 的啓動部分開始分析:
首先,OpenERP 啓動相關的服務, 這時並沒有建立數據庫鏈接和載入對象
1 2 3 4 5 6 7 8 |
|
不過可以在配置文件中指定 ‘db_name’ 參數,可以讓 OpenERP 在啓動時加載指定數據庫的對象並啓動 Cron。 實際生產環境中建議啓用該參數,否則需要在啓動OpenERP後,登錄一次OpenERP 在會加載對象並啓動CRON
1 2 3 |
|
不指定 ‘db_name’ 參數情況下,OpenERP 會直到用戶登錄時纔會初始化指定數據庫。
1 2 3 4 |
|
打開 get_db 和 get_db_and_pool 定義
1 2 3 4 5 6 7 8 9 |
|
順藤摸瓜,看RegistryManager.new 定義
1 2 3 4 5 6 |
|
然後看到 load_modules, 終於要加載了,嘿嘿。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
這裏,第一步是加載核心模塊 [‘base’],第二步加載需要升級或預載的模塊,第三步加載已安裝的模塊。實際加載語句是:
1
|
|
查看 load_marked_module 定義:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
重點是 load_module_graph
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
主要是下面2句,
1 2 3 |
|
先看 load_openerp_module
1 2 3 4 5 6 7 8 9 |
|
上面代碼中 import 了一個模塊。如果您看過 digitalsatori 校長的大作 OpenERP與Python 元編程, 下面就涉及到元類了:
import 的時候 就會調用元類的構造函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
上面的代碼基本上就是將自身類加入到 module_to_models 字典中。
然後我們來看pool.load
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
這裏我們可以看到 MetaModel 的身影,cls.create_instance(self, cr) 這裏就是動態構造對象的核心代碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
|
上面代碼很重要,可以看到首先是判斷該對象是否有繼承父類,如果沒有就直接構造,和動態沒有什麼關係。
如果有繼承父類, 就複製父類屬性, 這裏就是動態構建類的做法。
假如有不同模塊,都繼承了同一個父類,那麼如何保證類成員和屬性是否加載完整或覆蓋呢? 答案在於這句代碼:
1
|
|
Registry.get 的定義
1 2 3 |
|
最後看看obj.init(pool, cr)初始化對象,做了什麼動作?
1 2 3 4 5 6 7 8 9 10 11 12 |
|
pool.add(self._name, self) 定義如下:
1 2 3 |
|
到這裏應該很非常清楚,Registry.models 保存了對象的 model 信息。這樣多個對象繼承同一父類時,按照加載順序先後動態構建相關的類。
至此,OpenERP 啓動時動態加載模塊分析完成。如模塊安裝、升級、卸載等, 則是通過 signal_registry_change 和 check_registry_signaling 處理,重新載入 Registry, 然後重新構建 OpenERP 對象。