设计一个可拔插的 IOC 容器

前言

磨了许久,借助最近的一次通宵上线 cicada 终于更新了 v2.0.0 版本。java

之因此大的版本号变为 2,确实是向下不兼容了;主要表现为:git

  • 修复了几个反馈的 bug
  • 灵活的路由方式。
  • 可拔插的 IOC 容器选择。

其中重点是后面两个。github

新的路由方式

先来看第一个:路由方式的更新。数据库

在以前的版本想要写一个接口必须的实现一个 WorkAction;并且最麻烦的是一个实现类只能作一个接口。json

所以也有朋友给我提过这个 issue性能


因而改进后的使用方式以下:测试

是否有点似曾相识的感受😊。spa

如上图所示,不须要实现某个特定的接口;只须要使用不一样的注解便可。prototype

同时也支持自定义 pojo, cicada 会在调用过程当中对参数进行实例化。3d

拿这个 getUser 接口为例,当这样请求时这些参数就会被封装进 DemoReq 中.

http://127.0.0.1:5688/cicada-example/routeAction/getUser?id=1234&name=zhangsan

同时获得响应:

{"message":"hello =zhangsan"}
复制代码

实现过程也挺简单,你们查看源码便会发现;这里贴一点比较核心的步骤。

  • 扫描全部使用 @CicadaAction 注解的类。
  • 扫描全部使用 @CicadaRoute 注解的方法。
  • 将他们的映射关系存入 Map 中。
  • 请求时根据 URLMap 中查找这个关系。
  • 反射构建参数及方法调用。

扫描类以及写入映射关系


请求时查询映射关系


反射调用这些方法

是否须要 IOC 容器

上面那几个步骤其实我都是一把梭写完的,但当我写到执行具体方法时感受有点意思了。

你们都知道反射调用方法有两个重要的参数:

  • obj 方法执行的实例。
  • args.. 天然是方法的参数。

我第一次写的时候是这样的:

method.invoke(method.getDeclaringClass().newInstance(), object);
复制代码

而后一测试,也没问题。

当我写完以后 review 代码时发现不对:这样这里每次都会建立一个新的实例,并且反射调用 newInstance() 效率也不高。

这时我不自觉的想到了 Spring 中 IOC 容器,和这里场景也很是的相似。

在应用初始化时将全部的接口实例化并保存到 bean 容器中,当须要使用时只须要从容器中获取便可。

这样只是会在启动时作不少加载工做,但造福后代啊。

可拔插的 IOC 容器

因而我打算本身实现一个这样的 bean 容器。

但在实现以前又想到一个 feature:

不如把实现 bean 容器的方案交给使用者选择,能够选择使用 bean 容器,也能够就用以前的每次都建立新的实例,就像 Spring 中的 prototype 做用域同样。

甚至能够自定义容器实现,好比将 bean 存放到数据库、Redis 都行;固然通常人也不会这么干。

SPI 的机制也有点相似。

要实现上述的需求大体须要如下步骤:

  • 一个通用的接口,包含了注册容器、从容器中获取实例等方法。
  • BeanManager 类,由它来管理具体使用哪一种 IOC 容器。

因此首先定义了一个接口;CicadaBeanFactory:

包含了注册和获取实例的接口。

同时分别有两个不一样的容器实现方案。

默认实现;CicadaDefaultBean

也就是文中说道的,每次都会建立实例;因为这种方式其实根本就没有 bean 容器,因此也不存在注册了。

接下来是真正的 IOC 容器;CicadaIoc

它将全部的实例都存放在一个 Map 中。

固然也少不了刚才提到的 CicadaBeanManager,它会在应用启动的时候将全部的实例注册到 bean 容器中。

重点是图中标红的部分:

  • 须要根据用户的选择实例化 CicadaBeanFactory 接口。
  • 将全部的实例注册到 CicadaBeanFactory 接口中。

同时也提供了一个获取实例的方法:

就是直接调用 CicadaBeanFactory 接口的方法。


而后在上文提到的反射调用方法处就变为:

bean 容器中获取实例了;获取的过程能够是每次都建立一个新的对象,也能够是直接从容器中获取实例。这点对于这里的调用者来讲并不关心

因此这也实现了标题所说的:可拔插

为了实现这个目的,我将 CicadaIoc 的实现单独放到一个模块中,以 jar 包的形式提供实现。

因此若是你想要使用 IOC 容器的方式获取实例时只须要在你的应用中额外加入这个 jar 包便可。

<dependency>
    <groupId>top.crossoverjie.opensource</groupId>
    <artifactId>cicada-ioc</artifactId>
    <version>2.0.0</version>
</dependency>
复制代码

若是不使用则是默认的 CicadaDefaultBean 实现,也就是每次都会建立对象。

这样有个好处:

当你本身想实现一个 IOC 容器时;只须要实现 cicada 提供的 CicadaBeanFactory 接口,并在你的应用中只加入你的 jar 包便可。

其他全部的代码都不须要改变,即可随意切换不的容器。

固然我是推荐你们使用 IOC 容器的(其实就是单例),牺牲一点应用启动时间带来后续性能的提高是值得的。

总结

cicada 的大坑填的差很少了,后续也会作一些小功能的迭代。

尚未关注的朋友赶忙关注一波:

github.com/TogetherOS/…

PS:虽然没有仔细分析 Spring IOC 的实现,但相信看完此篇的朋友应该对 Spring IOC 以及 SpringMVC 会有一些本身的理解。

你的点赞与分享是对我最大的支持

相关文章
相关标签/搜索