徐昊 · TDD 项目实战 70 讲
徐昊
Thoughtworks 中国区 CTO
18159 人已学习
新⼈⾸单¥98
登录后,你可以任选4讲全文学习
课程目录
已完结/共 88 讲
实战项目二|RESTful开发框架:依赖注入容器 (24讲)
实战项目三|RESTful Web Services (44讲)
徐昊 · TDD 项目实战 70 讲
15
15
1.0x
00:00/00:00
登录|注册

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

你好,我是徐昊。从今天开始,让我们进入实战中的 TDD 环节。也就是使用 TDD 的方式,去实现我们工作中常用的技术框架。
之所以选择常用的技术框架,而不虚构某个业务系统,主要是因为 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 为主要功能参考,并对其适当简化,以完成我们的目标。
当我们完成全部功能之后,可以通过类似以下的代码实现 RESTful API:
package geektime.tdd.resources;
import geektime.tdd.model.Student;
import geektime.tdd.model.StudentRepository;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/students")
public class StudentsResource {
private StudentRepository repository;
@Inject
public StudentsResource(StudentRepository repository) {
this.repository = repository;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Student> all() {
return repository.all();
}
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response findById(@PathParam("id") long id) {
return repository.findById(id).map(Response::ok)
.orElse(Response.status(Response.Status.NOT_FOUND)).build();
}
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

徐昊介绍了实战中的TDD环节,以开发RESTful API的框架为例进行讲解。他强调TDD的难点在于理解需求并将其分解为功能点,因此选择常用的技术框架进行讲解。文章主要涵盖了支撑RESTful API的开发框架,包括依赖注入容器和支持RESTful API构建的Web框架。作者以Jakarta EE中的Jakarta Dependency Injection和Jakarta RESTful Web Services为主要功能参考,并对其进行适当简化。读者可以通过实现RESTful API的代码来加深理解。文章还提出了思考题,鼓励读者自行练习功能点的分解,并欢迎读者分享想法和思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《徐昊 · TDD 项目实战 70 讲》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • 🐑
    置顶
    TDD专栏福利大合集: 1、打卡赢好礼(4月23日-5月10日):正在进行中,学习专栏第1-10讲并在留言区打卡,结束后奖励; 2、代码亲手评(5月底):预计打卡结束后启动,完成前10讲的打卡,即可提交代码练习作业,徐昊老师会亲自点评; 3、线上带你练:根据专栏更新节奏和老师时间安排确定,徐昊老师会线上带四个同学手把手地改代码,敬请期待! 具体活动介绍见 👉 http://gk.link/a/11jPi
    2022-04-28
  • Flynn
    //TODO 1.注入框架是否注册该对象 //TODO 2.constructor注入获取到的对象是期望的 //TODO 2.filed注入获取到的对象是期望的 //TODO 3.method注入获取到的对象是期望的 //TODO 4.同一scope生命周期是同一个对象 //TODO 5.不同scope生命周期是不同对象

    作者回复: 以功能分解来看 粒度有些粗

    2022-04-16
  • 奇小易
    > 功能梳理 ```text jakarata dependency injection 核心功能 Q: 组件的构造,指的是什么? 将该组件的实例注入到注入点中。 注入点的常见配置方式有以下三种。 1、构造器注入 2、字段注入 3、方法注入 Q: 依赖的选择,指的是什么? 指当一个组件的实现有多种时,该如何选择哪一个实现作为当前注入点的实例。 这种选择一方面会导致循环依赖的问题,JSR 330提供Provider接口来解耦它们的直接依赖,从而解决该问题。 另一方面在有不同实现时,可以使用Named注解来标记不同的实现,从而确保实例的选择是符合预期的。 Q: 生命周期的控制,指的是什么? 指一个组件在容器中从创建,到最终的消亡,这整个过程如何实现。 其中需要支持单例和多例的组件构造模式。 ``` > 任务列表梳理 ```text TDD工作流程回顾。 首先要基于业务理解分解出功能点,另一方面需要基于架构愿景分解出功能上下文。 最终再在每个功能上下文中分解出具体的任务项。 基于上述内容,可知从业务上核心功能点就是组件的构造、依赖的选择、生命周期的控制。 而对于架构愿景没有很明确的思路,故将功能点作为功能上下文。 此时功能上下文分别是 组件的构造 依赖的选择 生命周期的控制 要将功能上下文分解为任务项,需要思考对外接口以及具体实现方式,最终分解出最小功能单元的测试任务。 "组件的构造" 1、对外接口:在构造器、方法、字段上添加对应注解即可 2、实现思路:扫描指定路径下所有Java文件的注解,分别基于不同类型的注入点,来分别进行处理。 构造器注入 Happy Path: 如果当前类的构造方法中存在"注入注解",则在容器中创建它之前,给这个参数实例化后再创建。 (感觉这步子有点大) Sad Path: 字段注入 ... 方法注入 ... ```
    2022-05-04
    3
  • aoe
    笔记 https://wyyl1.com/post/19/07/ 希望留言可以支持 Markdown 格式,这样更容易阅读 ## 功能点分解 ### 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 中类由容器统一管理 单例模式下创建对象需要考虑内存消耗、线程安全的问题
    2022-04-14
    3
  • davix
    請老師指導下go programmer怎麼學、練習這個項目。
    2022-05-03
    2
  • leesper
    思考题:因为暂时不存在架构愿景,因此可以把功能点当成功能上下文:组件构造、依赖选择、生命周期管理
    2023-01-21归属地:贵州
    1
  • 用一个一般复杂度的业务系统更有实用价值,更利于大家跟随、模仿
    2022-07-02
    1
  • 霜期飞敛
    - 组件的构造 - 扫描指定目录的所有类,识别出所有带有注解@Inject的类 - sad path:不同包名下的同名class,通过加上包名区分 - 解析类的注解元素 @Inject,进行归类,用于后续各个阶段的注入 - 构造器 - sad path:多个@Inject 注解的构造器,抛出异常 - 字段 - 方法 - 实例的名称 - 指定的方式 - sad path:指定多个同名实例,抛出异常 - default value - 默认自动生成的方式:默认类名(首字母小写)的方式 - 如果有多个 class#1 class#2 这样的方式累加 - injected constructor - 实例化 - 查找已存在的依赖 - 通过构造器实例化并注入 - sad path:循环依赖 - injected field - 实例化 - 属性设置 - 依赖查找对应的实例 - 通过反射实现字段注入 - sad path:循环依赖 - method field - 实例化 - 属性设置 - 依赖查找对应的实例 - 通过反射调用方法注入 - dependency selection - cycle dependency - proxy 实现解决循环依赖问题 - provider(factory) 延迟加载的循环依赖问题 - by tag - by name - by annotation - sad path: - 找不到对应的实例 - by name - by annotation type - 找到符合条件的多个实例 - 通过 @Primary 来进行优先级划分返回 - sad path:没有 @Primary,抛出异常 - 生命周期控制 - @Scope - singleton 单例实现,多次获取Class的实例返回一个实例 - prototype 和默认情况相同 - default value: prototype
    2022-04-30
    1
  • davix
    學這門課最遺憾的是身為go程序員,不知道Java 這些都是啥
    2022-04-15
    1
    1
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部