依赖注入框架并不神秘,其实它是非常简单的东西。不要去看 spring 的依赖注入源码,因为你只要一去看就意味着你再也写不敢下手自己撸了,它的功能因为过于强大,所以设计也过于复杂,普通程序员一眼看去只能望洋兴叹。
我也并没有去细致阅读 spring 源码。即便如此也只用了半天的时间便自己撸了一个基本满足标准依赖注入规范「 JSR-330 」的小框架 iockids。这个小框架只有一个主类 Injector,大约 200 行代码,它具备以下功能。
我们看一个稍微复杂一点的使用示例
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import iockids.Injector;
@Singleton
class Root {
@Inject
@Named("a")
Node a;
@Inject
@Named("b")
Node b;
@Override
public String toString() {
return String.format("root(%s, %s)", a.name(), b.name());
}
}
interface Node {
String name();
}
@Singleton
@Named("a")
class NodeA implements Node {
@Inject
Leaf leaf;
@Inject
@Named("b")
Node b;
@Override
public String name() {
if (b == null)
return String.format("nodeA(%s)", leaf);
else
return String.format("nodeAWithB(%s)", leaf);
}
}
@Singleton
@Named("b")
class NodeB implements Node {
Leaf leaf;
@Inject
@Named("a")
Node a;
@Inject
public NodeB(Leaf leaf) {
this.leaf = leaf;
}
@Override
public String name() {
if (a == null)
return String.format("nodeB(%s)", leaf);
else
return String.format("nodeBWithA(%s)", leaf);
}
}
class Leaf {
@Inject
Root root;
int index;
static int sequence;
public Leaf() {
index = sequence++;
}
public String toString() {
if (root == null)
return "leaf" + index;
else
return "leafwithroot" + index;
}
}
public class Demo {
public static void main(String[] args) {
var injector = new Injector();
injector.registerQualifiedClass(Node.class, NodeA.class);
injector.registerQualifiedClass(Node.class, NodeB.class);
var root = injector.getInstance(Root.class);
System.out.println(root);
}
}
上面这份代码用到了 iockids 提供的所有功能。
为了便于理解上述代码,我画了依赖图
上面的代码输出如下
root(nodeAWithB(leafwithroot0), nodeBWithA(leafwithroot1))
从这个输出中,我们也可以大致想象出依赖结构。
iockids 提供了丰富的注入错误异常报告,防止用户注入配置出错。
比如我们将上面的 NodeA 和 NodeB 的名称都配置成一样的 a,就会曝出下面的错误堆栈
iockids.InjectException: duplicated qualifier javax.inject.Named with the same class iockids.demo.Node
at iockids.Injector.registerQualifiedClass(Injector.java:87)
at iockids.Injector.registerQualifiedClass(Injector.java:70)
at iockids.demo.Demo.main(Demo.java:106)
如果我们将 NodeB 的构造器随意加一个参数
@Inject
public NodeB(Leaf leaf, int k) {
this.leaf = leaf;
}
运行时就会抛出下面的错误
iockids.InjectException: no accessible constructor for injection class int
at iockids.Injector.createNew(Injector.java:120)
at iockids.Injector.createNew(Injector.java:94)
at iockids.Injector.createFromParameter(Injector.java:167)
at iockids.Injector.createFromConstructor(Injector.java:145)
at iockids.Injector.createNew(Injector.java:123)
at iockids.Injector.createFromQualified(Injector.java:216)
at iockids.Injector.createFromField(Injector.java:173)
at iockids.Injector.injectMembers(Injector.java:233)
at iockids.Injector.createNew(Injector.java:136)
at iockids.Injector.createFromQualified(Injector.java:216)
at iockids.Injector.createFromField(Injector.java:173)
at iockids.Injector.injectMembers(Injector.java:233)
at iockids.Injector.createNew(Injector.java:136)
at iockids.Injector.createNew(Injector.java:94)
at iockids.Injector.getInstance(Injector.java:245)
at iockids.demo.Demo.main(Demo.java:107)
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.