OpenERP 模块动态加载原理及启动代码分析
- 版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
- 作者:步科
- 本文地址:http://buke.github.io/blog/2013/02/26/openerp-dynamic-loading-and-booting-way/
一般来说我们在编程中,对象定义都是预先定义好的。一些 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 对象。