本节摘要:十分钟左右搭起一个能跑的 Spring Boot 工程,并配上让请求旅程"现形"的三件观察工具:请求日志、调用栈断点、执行耗时埋点。后续各章的实验都在这个工程里做,不必再分心环境问题。
新建 Maven 工程,父依赖交给 Spring Boot:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
启动类与探针接口:
@SpringBootApplication public class TraceApplication { public static void main(String[] args) { SpringApplication.run(TraceApplication.class, args); } } @RestController class PingController { @GetMapping("/ping") public String ping() { return "pong"; } }
浏览器访问本地端口的 ping 路径返回 pong,实验场就绪。这里值得留意的是 @SpringBootApplication 干了什么:它组合了自动配置、组件扫描两个开关,把第 3 章要讲的容器装配工作自动完成了一大半。眼下只需知道:启动日志的最后一行出现在控制台时,请求旅程的所有关卡都已建好,静候第一个请求。
其一:请求进出日志。 给容器配一个最简的访问日志,让每个请求的到达与离开有据可查:
server: tomcat: accesslog: enabled: true pattern: '%t %a "%r" %s %D ms' spring: application: name: trace-lab
其二:调用栈断点。 在 ping 方法上打断点发起请求,IDE 会冻结那根容器线程并展示完整调用栈。从栈底往上读:线程入口 → 连接器处理 → 分发 → 你的方法。这张栈就是 1.1 节九站旅程的实物证据。
其三:最简耗时过滤器。 自己写一个过滤器,在旅程最外层计时报时:
@Component public class TimingFilter extends OncePerRequestFilter { private static final Logger log = LoggerFactory.getLogger(TimingFilter.class); @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws ServletException, IOException { long start = System.nanoTime(); try { chain.doFilter(req, resp); } finally { log.info("{} 耗时 {} 微秒", req.getRequestURI(), (System.nanoTime() - start) / 1000); } } }
把耗时打点放在过滤器这一层,量到的是"控制器之前的一切 + 控制器本身",与控制器内部日志的耗时相减,就能估算出旅程前半段(分发、映射、拦截)的开销。

💡 一个工程用到底:后续章节往这个工程里逐章加依赖(数据访问、安全、消息),每加一章就重放同一条请求,观察旅程多出的新关卡。这比每章新建工程更能看清"模块如何改变旅程"。
实验场虽小,第一次跑失败却是常态。下面四个真实高频故障,逐一给出定位路径。
第一种:端口被占用,启动日志出现绑定异常后进程退出。定位:看异常信息里的端口号,换一个端口或停掉占用进程即可。第二种:包位置放错,启动成功但访问 ping 返回 404。背景:组件扫描以启动类所在包为根,控制器放在兄弟包之外就扫不到。操作:把控制器移进启动类的子包,或用注解显式扩大扫描范围。结果:404 消失。第三种:依赖下载中断导致构建失败,表现为仓库路径下的临时文件残留。操作:删掉本地仓库中对应目录后重新构建。第四种:Java 版本不符,启动即报版本错误。Boot 3.x 要求 Java 17 以上,用老版本运行时最先暴露的就是它。
把四种故障记录下来很有价值:它们分别对应旅程的四个不同层——网络层(端口)、装配层(扫描)、构建层(依赖)、运行环境(版本)。此后任何一次启动失败,先判断它发生在哪一层,再动手排查,这个习惯比记住具体命令更重要。
最后补一条工作习惯:实验场工程建好后提交到版本库时,把访问日志目录排除在外,避免日志文件污染仓库历史;计时过滤器建议保留,它是后续每一章观察耗时的公共基础设施。环境搭建是全书投入产出比最高的一小时——此后任何机制存疑,都能在这个工程里两分钟内得到实证。