12|实战中的TDD:RESTful API的开发框架

TDD 学习笔记 | 极客时间 | 徐昊·TDD 项目实战 70 讲

TDD 的难点首先在于理解需求,并将需求分解为功能点

RESTful API 的开发框架

  • 场景:支撑 RESTful API 的开发框架,可以想象成下面两个场景
    • mini 版本的 Dropwizard
    • Spring MVC
  • 功能
    • 一个依赖注入容器(Dependency Injection Container/IoC Container)
    • 一个支持 RESTful API 构建的 Web 框架
  • 目标:以 Jakarta EE 中的 Jakarta Dependency Injection 和 Jakarta RESTful Web Services 为主要功能参考,并对其适当简化

依赖注入容器的大致功能

  • 关于依赖注入的来龙去脉可以参看 Martin Fowler 在 2004 年写的文章 《IoC 容器与依赖注入模式》
  • Jakarta Dependency Injection 的功能主要分为四部分:
    • 组件的构造
    • 依赖注入方法
      • Constructor 注入
      • Field 注入
      • 方法注入
    • 依赖的选择
    • 生命周期控制
  • 容器上下文
    • 通过 tag 来区分不同的对象
      • 弱类型,重构时需要基于字符串查找
    • 自定义 Annotation
      • 强类型,对重构更友好
  • Scope 判断
    • 单例
    • 多例

相关技术

需要注意的问题

有依赖关系时的注入

两个构造器间的循环依赖

1
2
3
4
5
6
7
class A { 
   @Inject B b; 
} 

class B { 
   @Inject A a; 
} 

JSR330 中的解决方案:通过标准的接口实现注入

1
2
3
4
5
6
7
class A { 
   @Inject Provider<B> b; 
} 

class B { 
   @Inject Provider<A> a; 
} 

进阶功能

  • 配置容器如何配置(可用 DSL 实现可配置的依赖注入)
  • 容器层级结构(Scope 上下文隔离)
  • 生命周期回调

功能点分解

happy path

自定义依赖注入的 Annotation

  • @Inject:标识这个类可以被容器管理(类似 Spring 的 @Component)
  • @Named:可以设置 String 类型的 tag 做唯一标识
  • @Scope:标识容器创建的对象是单例、多例的标签
  • 支持自定义 Annotation 被容器加入依赖管理(组合 @Inject、@Named、@Scope 实现自定义功能)
  • @Constructor:Constructor 注入标签
  • @Field:Field 注入标签
  • @Method:方法注入标签

将标记的类找到备用

  • 扫描项目中所有的类
  • 提取出所有标记了依赖注入标签的类的信息
    • 类名称
    • 类的完整路径
    • 类的所有构造器
    • 类上的所有 Annotation
    • 字段上的所有 Annotation
    • 方法上的所有 Annotation
  • 解析容器支持的自定义 Annotation
  • 将自定义 Annotation 转换为普通的 Annotation
  • 收集容器相关的 Annotation 类的信息
  • 将这些类的信息存储在容器中

代理类

  • 通过代理类实现 Provided 解决循环依赖问题

通过容器获取实例

  • 根据 Class 在容器中找到对应的类,返回实例
  • 根据 @Named 中的 tag,找到对应的类,返回实例
  • 根据 自定义 Annotation 找到对应的类,返回实例
  • 利用反射注入:Field 实例
  • 利用反射注入:方法参数实例

default path

  • @Named:默认去类名(首字母小写)
  • @Scope:默认为多例

sad path

没有匹配到对象

  • Class 没有被容器管理
  • @Named 没有匹配的字符串
  • 不同包下,类名一样
  • 多个 @Named 重名
  • 不支持第三方 jar 中类由容器统一管理

单例模式下创建对象需要考虑内存消耗、线程安全的问题

comments powered by Disqus