BPMN-JS中的依赖注入设计
2012 年 11 月 9 日
为了扩展BPMN.JS的功能,我们通常在Modeler/Viewer实例化时通过传入 additionalModules
参数的方法来扩展/修改BPMN-JS的功能。

BPMN-JS通常通过类似下面的代码来自定义DI(依赖注入)模块:
export default class CustomRenderer extends BaseRenderer { constructor(eventBus, bpmnRenderer) { super(eventBus, HIGH_PRIORITY); this.bpmnRenderer = bpmnRenderer; } canRender(element) { return false; } drawShape(parentNode, element) { ... } getShapePath(shape) { ... return shape } } CustomRenderer.$inject = ['eventBus', 'bpmnRenderer']; export default { __init__: ['CustomRenderer'], CustomRenderer: ['type', CustomRenderer] }
上面代码实现了一个继承自 BaseRenderer
的 CustomRenderer
类, BaseRenderer
类是diagram-js包中实现的一个渲染基类(抽象类),里面定义了一些空的原型方法。通过上面的写法,我们就可以覆盖掉bpmn-js中 BaseRenderer
类的实现,从而实现了bpmn-js自定义渲染功能。
另外上面代码 CustomRenderer.$inject = ['eventBus', 'bpmnRenderer'];
而这实际上是bpmn.js依赖的diagram-js包所依赖的 didi
实现的依赖注入。
didi
是用JavaScript实现的一个依赖注入/控制反转容器。该库对外暴露了如下4个接口
- annotate
- parseAnnotations
- Module
- Injector
annotate
该方法用来将形如 ['a', 'b', 'c', function(a, b, c) {}]
通用的依赖注入写法,转换为 didi
特定的语法:
import { annotate } from "didi"; var fn = annotate(['a', 'b', 'c', function(a, b, c) {}]); // 或 var fn = annotate('a', 'b', 'c', function(a, b, c) {}); // 输出结果为 var fn = function(a, b, c) {}; fn.$inject = ['a', 'b', 'c'];
parseAnnotations
该方法用来解析JavaScript函数的形参到一个数组,示例:
function Human(name, age) { this.name = name; this.age = age; } var res = parseAnnotations(Human); // 结果 ['name', 'age']
Module
这是一个类,didi提供的自定义module接口,示例:
var { Module } = require('didi'); var carModule = new Module(); carModule.type('car', Car).factory('engine', createPetrolEngine).value('power', 1184); // 或者通过对象字面量的方式来定义didi要注册的module var carModule = { 'car': ['type', Car], 'engine': ['factory', createPetrolEngine], 'power': ['value', 1184] }; // 注册到Injector var injector = new Injector([module]);
Injector
注入器,可以将上面定义的模块,通过一个数组参数实例化进该容器
const injector = new Injector([ carModule ]) // get injector.get('car').start(); // invoke injector.invoke(['car', function(car) { car.start(); }]); // 根据类的形参,在locals中查找依赖,并在类实例化时将形参依赖进行注入 injector.instantiate(function(a, b, c) {}, locals); // injector.createChild(modules, forceNewInstances);