我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。
现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?
到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。
当前回答
它应该像这样灵活和简单:
var MyClass1 = function () {}
var MyClass2 = function (myService1) {
// myService1.should.be.instanceof(MyClass1);
}
container.register('myService1', MyClass1);
container.register('myService2', MyClass2, ['myService1']);
我写过一篇关于node.js中的依赖注入的文章。
管理你的服务-node.js依赖注入 包文档在这里
我希望它能帮到你。
其他回答
我认为我们仍然需要Nodejs中的依赖注入,因为它放松了服务之间的依赖关系,使应用程序更加清晰。
受Spring Framework的启发,我也实现了自己的模块来支持Nodejs中的依赖注入。我的模块还能够检测代码更改并自动重新加载服务,而无需重新启动应用程序。
访问我的项目:Buncha - IoC容器
谢谢你!
TypeDI是这里提到的最可爱的,看看TypeDI中的代码
import "reflect-metadata";
import {Service, Container} from "typedi";
@Service()
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();
看看这段代码:
import {Container, Service, Inject} from "typedi";
// somewhere in your global app parameters
Container.set("authorization-token", "RVT9rVjSVN");
@Service()
class UserRepository {
@Inject("authorization-token")
authorizationToken: string;
}
Node.js和其他平台一样需要DI。如果您正在构建一些大的东西,DI将使您更容易模拟代码的依赖关系并彻底测试代码。
例如,数据库层模块不应该只在业务代码模块中使用,因为在单元测试这些业务代码模块时,dao将加载并连接到数据库。
一种解决方案是将依赖项作为模块参数传递:
module.exports = function (dep1, dep2) {
// private methods
return {
// public methods
test: function(){...}
}
}
通过这种方式,依赖关系可以轻松自然地模拟,你可以专注于测试你的代码,而不需要使用任何棘手的第三方库。
还有其他的解决方案(百老汇,建筑师等)可以帮助你解决这个问题。尽管他们可能做的比你想要的多,或者使用更多的杂物。
我还写了一个模块来完成这一点,它被称为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()完全相同(除了您的修改)。此外,完全支持调试。
简而言之,您不需要像在c# /Java中那样的依赖注入容器或服务定位器。因为Node.js利用了模块模式,所以没有必要执行构造函数或属性注入。尽管你仍然可以。
JS的伟大之处在于你可以修改任何东西来达到你想要的效果。这在测试时非常有用。
看看我这拙劣的例子吧。
MyClass.js:
var fs = require('fs');
MyClass.prototype.errorFileExists = function(dir) {
var dirsOrFiles = fs.readdirSync(dir);
for (var d of dirsOrFiles) {
if (d === 'error.txt') return true;
}
return false;
};
MyClass.test.js:
describe('MyClass', function(){
it('should return an error if error.txt is found in the directory', function(done){
var mc = new MyClass();
assert(mc.errorFileExists('/tmp/mydir')); //true
});
});
注意到MyClass是如何依赖fs模块的吗?正如@ShatyemShekhar提到的,你确实可以像在其他语言中一样进行构造函数或属性注入。但在Javascript中这是不必要的。
在这种情况下,您可以做两件事。
您可以存根fs。方法,或者在调用require时返回一个完全不同的模块。
方法1:
var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;
方法2:
var oldrequire = require
require = function(module) {
if (module === 'fs') {
return {
readdirSync: function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
};
} else
return oldrequire(module);
}
关键是要利用Node.js和Javascript的强大功能。注意,我是一个CoffeeScript的人,所以我的JS语法可能是不正确的。我并不是说这是最好的方法,但这确实是一种方法。Javascript大师们或许能提供其他解决方案。
更新:
这应该可以解决您关于数据库连接的特定问题。我将创建一个单独的模块来封装数据库连接逻辑。就像这样:
MyDbConnection.js:(请务必选择一个更好的名称)
var db = require('whichever_db_vendor_i_use');
module.exports.fetchConnection() = function() {
//logic to test connection
//do I want to connection pool?
//do I need only one connection throughout the lifecyle of my application?
return db.createConnection(port, host, databasename); //<--- values typically from a config file
}
然后,任何需要数据库连接的模块都会包含MyDbConnection模块。
SuperCoolWebApp.js:
var dbCon = require('./lib/mydbconnection'); //wherever the file is stored
//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is
//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database.
不要一字不差地照搬这个例子。这是一个蹩脚的例子,试图说明您利用模块模式来管理依赖项。希望这对你们有帮助。