依赖注入框架并不神秘,其实它是非常简单的东西。不要去看 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)
1 mjStudio 2018-05-02 09:26:07 +08:00 Android 开发路过 |
![]() | 2 omengye 2018-05-02 09:26:41 +08:00 大佬的 trySetAccessible 方法是只能用在 Java9 以上版本? |
![]() | 3 shellquery OP 准确的讲,整个项目只能运行在 java10,注意 var 关键字 |
![]() | 4 shellquery OP ![]() https://github.com/pyloque/httpkids 自己撸 web 框架 https://github.com/pyloque/ormkids 自己撸 orm 框架 https://github.com/pyloque/rpckids 自己撸 rpc 框架 还在犹豫什么呢,关注公众号「码洞」吧 |
![]() | 6 exalex 2018-05-02 11:31:37 +08:00 @shellquery 关注了 |
![]() | 7 arthas2234 2018-05-02 11:49:24 +08:00 。。。Java10,我们公司 Java8 都不愿意上 |
![]() | 8 shellquery OP @arthas2234 公司跟不上时代,但是我们自己可以跟得上 |
9 THP301 2018-05-03 12:07:53 +08:00 收藏了 |
10 AllOfMe 2018-05-04 09:43:04 +08:00 via iPhone 你这个照片看的我有点慌 |
![]() | 11 shellquery OP @AllOfMe 要的就是这效果 |