我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
当前回答
这取决于应用程序的设计。显然,你可以做一个类似java的注入,在那里创建一个类的对象,并将依赖项传递给构造函数,就像这样。
function Cache(store) {
this._store = store;
}
var cache = new Cache(mysqlStore);
如果你不是在javascript中做OOP,你可以做一个init函数来设置一切。
然而,还有另一种方法可以采用,这种方法在基于事件的系统(如node.js)中更为常见。如果您可以将应用程序建模为仅(大多数时候)对事件进行操作,那么您所需要做的就是设置所有内容(我通常通过调用init函数来完成)并从存根发出事件。这使得测试相当容易和易读。
其他回答
我还写了一个模块来完成这一点,它被称为rewire。只需使用npm安装rewire,然后:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
我受到Nathan MacInnes注射剂的启发,但使用了不同的方法。我不使用vm来评估测试模块,事实上我使用节点自己的要求。这样,您的模块的行为与使用require()完全相同(除了您的修改)。此外,完全支持调试。
我最近检查了这个线程,原因和OP差不多——我遇到的大多数库都临时重写了require语句。我用这种方法取得了不同程度的成功,所以我最终使用了下面的方法。
在一个快速应用程序的上下文中-我将app.js包装在bootstrap.js文件中:
var path = require('path');
var myapp = require('./app.js');
var loader = require('./server/services/loader.js');
// give the loader the root directory
// and an object mapping module names
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js'));
myapp.start();
传递给加载器的对象映射是这样的:
// live loader config
module.exports = {
'dataBaseService': '/lib/dataBaseService.js'
}
// test loader config
module.exports = {
'dataBaseService': '/mocks/dataBaseService.js'
'otherService' : {other: 'service'} // takes objects too...
};
然后,不要直接调用require…
var myDatabaseService = loader.load('dataBaseService');
如果加载器中没有别名,那么它将默认为常规require。 这样做有两个好处:我可以交换类的任何版本,并且消除了这种需要 在整个应用程序中使用相对路径名(因此如果我需要下面的自定义库 或以上的当前文件,我不需要遍历,并要求将缓存模块针对相同的关键)。它还允许我在应用程序的任何位置指定模拟,而不是在立即的测试套件中。
为了方便起见,我刚刚发布了一个小的npm模块:
https://npmjs.org/package/nodejs-simple-loader
事实上,你可以在没有IoC容器的情况下测试node.js,因为JavaScript是一种真正动态的编程语言,你可以在运行时修改几乎所有东西。
考虑以下几点:
import UserRepository from "./dal/user_repository";
class UserController {
constructor() {
this._repository = new UserRepository();
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
因此,您可以在运行时覆盖组件之间的耦合。我认为我们应该以解耦JavaScript模块为目标。
实现真正解耦的唯一方法是删除对UserRepository的引用:
class UserController {
constructor(userRepository) {
this._repository = userRepository;
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
这意味着你需要在其他地方进行对象合成:
import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";
export default new UserController(new UserRepository());
我喜欢将对象组合委托给IoC容器的想法。您可以在文章《JavaScript中依赖倒置的当前状态》中了解更多关于此思想的内容。这篇文章试图揭穿一些“JavaScript IoC容器神话”:
误解1:JavaScript中没有IoC容器的位置 误解2:我们不需要IoC容器,我们已经有模块加载器了! 误解3:依赖倒置===注入依赖
如果你也喜欢使用IoC容器的想法,你可以看看InversifyJS。最新版本(2.0.0)支持多种用例:
内核模块 内核的中间件 使用类、字符串或符号作为依赖标识符 常数值注入 类构造函数的注入 工厂注入 汽车工厂 提供程序的注入(异步工厂) 激活处理程序(用于注入代理) 多注射 标记绑定 自定义标记装饰器 指定绑定 上下文绑定 友好的异常(例如循环依赖)
你可以在InversifyJS上了解更多。
看看dips(一个简单而强大的Node.js依赖注入和实体(文件)管理框架)
https://github.com/devcrust/node-dips
我认为我们仍然需要Nodejs中的依赖注入,因为它放松了服务之间的依赖关系,使应用程序更加清晰。
受Spring Framework的启发,我也实现了自己的模块来支持Nodejs中的依赖注入。我的模块还能够检测代码更改并自动重新加载服务,而无需重新启动应用程序。
访问我的项目:Buncha - IoC容器
谢谢你!