1. 为什么我在2024年才开始写JavaWeb学习日记
打开博客后台准备敲第一篇文章的时候,其实挺感慨的。做开发这几年,断断续续用过不少框架,也写过一些接口,但一直没系统梳理过JavaWeb这条线。直到最近重新翻自己早年记的零散笔记,才意识到很多当时"会用但不明白"的东西,其实一直是靠背诵应付过来的。
这个系列想写给两类人:一类是刚接触JavaWeb、被各种概念绕晕的学生,另一类是像我这样,能跑通代码但内心总觉得"根基不太稳"的半路开发者。我的目标是把这个领域里那些教科书讲得太抽象、培训班又讲得太功利的内容,用一个学习者的视角重新捋一遍。这一篇先不急着写代码,而是把Web开发这座大厦的整体轮廓描清楚——它有哪些楼层、每层住着谁、数据是怎么在楼里跑来跑去的。
开头先给一个最直白的结论:JavaWeb不是一门技术,而是一整条技术链的组合。很多人学不动的原因,不是某一环太难,而是始终不知道自己学的这个Servlet、那个Filter,在整个链路里到底站在哪、负责什么。所以这篇日记的第一章,我会先把这条链从用户浏览器到数据库完整走一遍,再逐层拆开。
注意:这是一篇长文,我不会为了"显得专业"堆术语,每个概念尽量用生活类比说明白,同时给出下一步可以去验证的实操入口。
2. 浏览器到服务器的第一公里:HTTP和URL在幕后做了什么
很多人学Web开发第一个接触的抽象概念就是"客户端—服务器",但很少人认真想过,你按下回车的那一刻,电脑到底做了几件事。我把这个过程拆成五步,每一步都可以通过浏览器自带的开发者工具亲眼验证。
2.1 从你输入网址到看到页面的完整旅程
假设你在地址栏输入了一个网址(为避开真实域名,这里我们用example-test.com代替)。浏览器并没有"嗖"地一下就连到了某台服务器,它做的事是:
- 先在本机缓存、系统hosts文件、本地DNS缓存里找这个域名对应的IP地址;
- 没找到就向最近的DNS服务器发起查询,拿到真正的IP;
- 浏览器向这个IP的80或443端口发起TCP连接;
- 连接建立后,组装一个HTTP请求报文发出去;
- 服务器返回HTTP响应报文,浏览器解析HTML、加载CSS和JS、渲染页面。
这五步听起来简单,但每一步都值得展开。以第二步的DNS解析为例,很多企业级问题都出在这个环节:配置了CDN之后访问不稳定,往往是不同地区DNS解析到了不同节点;本地开发时改了hosts没生效,往往是浏览器缓存了DNS记录。学习JavaWeb没必要一上来就深挖网络原理,但至少要清楚:HTTP是Web世界唯一的通用语言,JavaWeb就是这门语言的服务器端方言体系。
2.2 HTTP报文的真实长相:用浏览器工具亲眼看一下
我教过几个朋友看HTTP报文,最好的办法不是背文档,而是打开Chrome开发者工具(F12),切到Network面板,刷新一个网页。你会看到一整列请求,点开任意一个,右侧会分门别类展示请求头、响应头、载荷数据。
一个典型的GET请求大概长这样:
GET /index.html HTTP/1.1 Host: example-test.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Accept: text/html,application/xhtml+xml Connection: keep-alive对应响应可能是:
HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 4821 Date: Mon, 11 Mar 2024 08:30:00 GMT这两段报文其实就是纯文本,但恰恰是JavaWeb后端开发每天都要打交道的东西。比如Content-Type决定浏览器把响应当HTML还是JSON解析;Set-Cookie决定登录状态如何保存在客户端;Status Code的200、302、404、500分别代表什么,这些是后端开发的基本功。
这里分享一个我的"笨办法":刚学Servlet的时候,我写了一个故意只返回"hello world"字符串的接口,然后在响应头里轮流改Content-Type为text/plain和text/html,亲眼观察浏览器渲染结果的差异。这个实验花不了十分钟,但比背十遍"Content-Type的作用"都管用。
3. 静态页面与动态页面:区分JavaWeb到底"Web"在哪
在我还没系统学习JavaWeb之前,一直有个困惑:我会写HTML和CSS,也用过JavaScript,为什么还要专门学一套Java的东西?后来才想明白:HTML、CSS、JS这些是前端的"静态艺术",而JavaWeb的核心价值在于——在服务器上动态生成HTML,或者生成数据供前端动态渲染。
3.1 静态页面:写死了的文件
所谓静态页面,就是服务器上放着一个已经写好的.html文件,用户请求什么,服务器原样返回什么。就像你去一家餐厅,菜单是印刷好的,今天来是这个菜单,明天来还是这个菜单。
比如我本地放一个index.html,内容固定是:
<!DOCTYPE html> <html> <head><title>测试页</title></head> <body> <h1>欢迎访问</h1> <p>当前时间写死在文件里</p> </body> </html>不管谁访问、什么时候访问,看到的都是这一模一样的内容。这种模式简单高效,但对于需要定制内容的场景完全不够用——你不可能为每个用户手写一份不同的HTML文件。
3.2 动态页面:由程序现场加工出来的页面
动态页面则相反,服务器收到请求后,会根据参数、数据库里的数据,临时"拼装"出一份HTML返回给浏览器。还是餐厅类比:动态页面更像是厨师根据你点的菜现场炒制,同一个菜名,加辣不加辣,放葱不放葱,出来的成品不一样。
在JavaWeb里,早期实现动态页面的技术叫Servlet,后来有了JSP(Java Server Pages),再后来前后端分离兴起,动态内容不再直接拼HTML,而是返回JSON数据,由前端JavaScript负责渲染。但不管形式怎么变,底层的核心逻辑一致:
- 服务器接收请求;
- 解析请求参数(URL里的Query String、表单Body、请求头等);
- 调用Java代码处理业务逻辑(比如查数据库);
- 生成动态响应内容(HTML或JSON);
- 通过HTTP响应返回给浏览器。
3.3 一个最容易让初学者误解的点
很多人初学JavaWeb时会以为Servlet就是用来写HTML的,甚至觉得JSP是HTML的替代品。这其实是本末倒置。Servlet和JSP的定位都是"服务器端生成动态内容",它们本质是一家人:JSP在运行时会被容器翻译成Servlet。后面系列文章里我会专门写一篇Servlet生命周期和JSP九大内置对象的拆解,这里先记住结论:JSP本质是Servlet的"套壳模板",学透Servlet,JSP就只剩语法壳子。
4. JavaWeb技术栈全景图:从Servlet到Spring Boot
JavaWeb发展了几十年,技术栈层层叠加,新人入门时最头疼的问题就是:这么多框架和名词,到底先学哪个、哪个已经过时、哪个必须在简历里写?我根据自己的学习和面试经历,把JavaWeb技术栈按时间线画了一张"楼层图",帮大家建立坐标系。
4.1 底层地基:Servlet规范和Servlet容器
所有JavaWeb应用的基石是Servlet。需要注意,Servlet本身只是一套Java接口规范(javax.servlet.Servlet),定义了Java类如何接收请求、返回响应。真正干活的是Servlet容器(也叫Web容器),它负责网络监听、请求解析、Servlet实例创建和销毁。最常见的容器是Tomcat,之前还有Jetty、Undertow可选。
一个最基本Servlet类长这样:
import javax.servlet.http.*; import javax.servlet.*; import java.io.IOException; public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("<h1>第一个Servlet</h1>"); } }这段代码的重点不在语法,而在于它揭示了JavaWeb最原始的模型:继承HttpServlet、重写doGet/doPost、操作request和response两个对象。后续所有框架,本质上都是在这个流程外面包了一层又一层的"便利设施"。
4.2 繁荣期三件套:JSP、Filter、Listener
Servlet之后出现的三样配套技术,在今天依然有很多老项目在用:
- JSP:在HTML里嵌Java代码,服务器动态渲染页面。早期后端一人包揽前后端时期的绝对主力。
- Filter:过滤器,可以在请求到达Servlet之前、响应返回给客户端之前,对请求和响应做统一处理,最典型的应用是登录校验、字符编码设置。
- Listener:监听器,监听应用启动、停止、Session创建销毁等事件,常用于初始化全局配置。
这三样是JavaWeb的"经典原味",如果你看老项目,满眼都是它们的影子。但2024年的今天,新项目基本不会直接用JSP写页面了,转向了前后端分离架构。
4.3 框架时代:SSM到Spring Boot的演进
JavaWeb真正进入框架时代是从Spring开始的。Spring的核心思想是"容器管理对象"——你不需要自己new一个Service对象,而是告诉Spring什么类需要被管理、依赖怎么注入,Spring在应用启动时帮你组装好一切。这个概念就是IoC(控制反转)和DI(依赖注入),之所以重要,是因为它彻底改变了JavaWeb代码的组织方式:从"我们自己控制一切"变成"把控制权交给容器"。
SSM(Spring + Spring MVC + MyBatis)是2015年前后国内JavaWeb开发的标准套餐:
- Spring负责整体容器管理、事务、AOP;
- Spring MVC负责Web层,把请求分发到对应Controller方法;
- MyBatis负责数据库访问,把SQL和Java方法绑定。
再后来Spring Boot出现,它的最大贡献不是新概念,而是消灭了海量配置文件。以前配置一个Spring项目,要写XML数据源、包扫描、事务管理器、视图解析器,Spring Boot用"约定大于配置"的方式,加上自动配置机制,让项目几乎开箱即用的跑起来。现在的JavaWeb新项目,十有八九是Spring Boot起步。
4.4 一张表看懂当前主流技术选择
我根据在实际项目中的使用频率,列一个当前JavaWeb开发的技术选型参考表,给不同目标的人参考:
| 应用场景 | 主流选择 | 替代方案 | 我的备注 |
|---|---|---|---|
| Web接口开发 | Spring Boot + Spring MVC | JAX-RS | 新项目首选 |
| ORM/数据库访问 | MyBatis-Plus / Spring Data JPA | JDBC / JdbcTemplate | 根据团队习惯选 |
| 模板页面渲染 | Thymeleaf(服务端渲染) | JSP已基本退出 | 前后端分离则不用 |
| 安全权限 | Spring Security / Sa-Token | Shiro | 新手先学登录鉴权基础 |
| 接口文档 | Knife4j / springdoc-openapi | Postman+手写文档 | 团队协作必备 |
这个表格并不是教大家无脑追新,而是想说明一件事:JavaWeb的学习路线可以从Servlet学到Spring Boot,但不要迷失在框架丛林中。最重要的永远是HTTP模型、面向对象设计、数据库操作这几根支柱。
5. 环境准备是第一道坎:JDK、Tomcat、Maven和IDEA的配合关系
说实话,很多JavaWeb初学者不是被代码难倒的,而是被环境配置挡住了。这一章我想花点篇幅把环境这件"鸡毛蒜皮的小事"讲透,因为这是后续一切实操的地基,同时也能帮你避开一堆常见的坑。
5.1 先搞清楚这几个工具的关系
用一个餐厅来比喻我们的JavaWeb开发环境:
- JDK(Java Development Kit):就是厨师的厨艺工具箱,没有它,Java代码没法编译和运行。
- IDEA(开发工具):相当于厨房的操作台,帮你切菜(写代码)、配菜(管理项目结构)。
- Maven:相当于食材供应链管理,帮你从中央仓库(Maven中央仓库)拉取各种"食材"——也就是第三方jar包,同时管理它们的版本关系,让你不用手动满网找jar包再往lib目录里扔。
- Tomcat:相当于前厅服务员和后厨之间的传菜窗口,你的Java程序编译完后不是一个独立的EXE,而是一个Web应用(war或目录),必须被放进Tomcat这个容器里"托管",外部请求才会被正确送达。
这四个工具缺一不可,但它们各管各的,新手常见的误会是:以为装了IDEA就有了JDK,或者以为安装了Tomcat就等于装了服务器。实际上IDEA可以自动帮你管理JDK路径,但Tomcat需要单独下载、配置。
5.2 我推荐的最小环境清单
如果你现在从零开始,建议按这个版本组合装:
- JDK版本:8或17。JDK 8是老项目的长期主流,17是目前Spring Boot 3.x要求的基础版本。个人学习和新项目建议直接上17,避免以后踩"版本太低跑不了新框架"的坑。
- 开发工具:IntelliJ IDEA Community版就够,不需要盗版旗舰版,Community版足够支撑纯JavaWeb学习,创建项目时选择"Maven Archetype"的
maven-archetype-webapp模板即可。 - 构建工具:Maven 3.9.x,注意Maven本地仓库默认在用户目录
.m2下,国内网络建议配置阿里云镜像,否则下载依赖等到怀疑人生。 - 容器:Tomcat 9.x或10.x版本。有个坑,Tomcat 10后被Servlet规范移到了新包名
jakarta.*,很多老教程代码是javax.*,照着写会报类找不到,新手如果暂时不想纠结这个差异,用Tomcat 9配合JDK 8或17最顺。
5.3 第一个"Hello Web"应该怎么跑
环境装完,不少人又卡在"我代码写了,怎么看到效果"这一步。这里给一个最不容易出错的流程:
- IDEA里新建Maven Webapp项目,生成标准的
src/main/java和src/main/webapp/WEB-INF/web.xml结构; - 写一个最简单的Servlet类(参照上一章的示例代码);
- 在
web.xml里配置Servlet映射(或者用注解@WebServlet("/hello"),现在Tomcat支持注解方式,简洁很多); - 点击IDEA右上角配置Tomcat,选择"Deployment"标签,把当前项目Artifact挂到Tomcat上;
- 启动Tomcat,浏览器访问
http://localhost:8080/hello。
如果你每一步都照着做,却没有出现预期页面,大概率是卡在"Deployment"这一步——Artifact没选对,或者Application context配置错了路径。这类问题有个通用自查口诀:看IDEA下部日志控制台的Tomcat报错信息,比反复看代码更管用。我第一次配环境时绕了整整一天,后来总结下来,80%的启动失败问题,日志里都会明明白白写着ClassNotFoundException或Port already in use之类的关键句。
6. 写一个能处理请求的Servlet:理解HttpServletRequest和HttpServletResponse
理解了环境,现在进入JavaWeb的第一段真正的编码体验。这一章我打算用一个非常典型的业务场景——用户登录注册的简化版——来串起Servlet开发的核心操作。这样做的原因是:登录注册几乎涵盖Web后端最常用的功能模块,以此为切入点学Servlet,学到的每个知识点都能直接映射到现实开发。
6.1 从浏览器拿到用户参数
用户登录时需要把用户名和密码传给服务器。最常见的传参方式有两种:GET(URL后面跟?username=abc&password=123)和POST(请求体里传表单数据)。在Servlet里,取参数的方式完全一样:
@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write( "<h2>收到请求:" + username + "</h2>" ); } }这里有三个点值得停下来解释:
request.setCharacterEncoding("utf-8")是在读取Body参数之前调用的,如果放在getParameter之后再调用就来不及了。这个"先设置再读取"的顺序,是中文乱码问题最重要的避坑规则。response.getWriter()拿到的是向客户端输出文本的字符流,一定要在首次获得Writer之前设置ContentType,否则可能输出乱码。- 为什么用
doPost而不用doGet?因为登录信息属于敏感数据,POST不会被浏览器记录在URL历史里,也不容易被日志直接打印。
6.2 请求转发和重定向:两个看起来像但本质不同的跳转
写Web应用的时候,经常会遇到"登录成功后跳转到首页"这样的需求。JavaWeb里有两种实现方式:forward和sendRedirect。我当年分不清,后来用一句话记牢:forward是服务器内部转发,地址栏不变;sendRedirect是浏览器重新发起请求,地址栏会变。
具体来说:
// 服务器内部转发,浏览器地址栏依然是 /login request.getRequestDispatcher("/welcome").forward(request, response); // 302重定向,浏览器地址栏会变成 /welcome response.sendRedirect("welcome");选择哪种取决于业务场景。如果登录成功后只是想让用户看到结果页,用forward更快;如果登录后想彻底跳到另一个URL(比如防止表单刷新重复提交),用sendRedirect更合理。后续写登录拦截器的时候,这些概念都会反复出现。
6.3 踩坑记录:为什么我的表单提交后中文全是问号
分享一个我早期真实踩过的坑,也大概率是每个JavaWeb初学者都会遇到的:表单里输入中文,提交后控制台打印出来全是???。
排查思路是:
- 先看请求是不是POST并且Body带的是UTF-8编码——排除前端问题;
- 再检查Servlet代码里是否在
getParameter之前调用了setCharacterEncoding——这是原因; - 如果还是没有修复,再检查IDEA里Tomcat的VM options是不是设置了
-Dfile.encoding=GBK——这种情况会出现日志显示正常、程序里乱码的"双重乱码"问题。
我当时的情况就是第二种。因为在一个教程里看到别人写了setCharacterEncoding,但没注意到他写在了getParameter之后,导致我跟着抄也没生效。后来我养成一个习惯:凡是Servlet处理中文,第一行就是设置编码,不只对POST Body设UTF-8,还会在响应时设置同样的字符编码。这套组合拳打下来,乱码问题基本能规避九成。
7. Servlet生命周期和配置细节:容器比你想象中管得多
很多教程会把Servlet生命周期放到很靠前的位置讲,我反而想放在这里,因为只有你亲手写过一个Servlet并在服务器日志里看到那些输出,才能真正理解"生命周期"四个字的含义。
7.1 init、service、destroy三个关键时刻
Servlet的生命周期由容器(Tomcat)接管,我们作为开发者只需要继承HttpServlet并重写特定方法。生命周期主要经过三个阶段:
- init():Servlet被首次加载或首次请求到达时调用一次,常用于初始化资源(如加载配置文件、建立数据库连接池)。
- service():每次请求都会进入这个方法。HttpServlet已经帮我们实现好service,它会根据请求方法分发到doGet、doPost等重写方法里。
- destroy():应用关闭或Servlet被卸载时调用一次,常用于释放资源。
一个常见到令人意外的现象是:Servlet默认不是启动时就创建的,而是第一个请求到达时才实例化。所以很多人会写一个init()里加了打印的Servlet,启动Tomcat发现控制台没有输出,以为代码没生效。其实不是,只是你这个Servlet还没被"第一次使用"而已。想让容器启动时就初始化Servlet,可以在配置里指定<load-on-startup>1</load-on-startup>。
7.2 单实例多线程的并发模型
Servlet容器的一个经典设计是:每一个Servlet在容器中只有一个实例(单例),但它要同时服务所有并发请求,每个请求由独立线程执行。这意味着如果你在Servlet类里定义了成员变量用于存储用户数据,那么不同请求的线程会同时读写这个变量,极易产生并发安全问题。
我见过很多初学者写的代码:
public class BadServlet extends HttpServlet { private String username; // 危险! protected void doPost(...) { username = request.getParameter("username"); // 后续逻辑使用username } }如果请求A设置了username为"A",请求B紧接着设置了"B",然后A的逻辑继续往下执行时读到的可能就不是"userA"了,结果就是数据串号。正确的做法是:Servlet类不存请求相关的可变状态,数据一律通过方法参数或局部变量传递。这一点不只是面试爱问,项目写多了迟早会遇到。
7.3 从web.xml到注解:配置方式的演进
早期Servlet必须在web.xml里按这个方式配置:
<servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>com.example.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>现在Tomcat支持注解简化:
@WebServlet(name = "loginServlet", urlPatterns = "/login") public class LoginServlet extends HttpServlet { ... }两者作用是等价的:urlPatterns就是映射的URL。不过我的建议是,初学阶段还是把web.xml的配置方式亲手写几遍。因为等你后面学Spring MVC时,会看到很多"前端控制器DispatcherServlet映射""字符编码过滤器配置"之类的概念,如果连最原始的web.xml都看不懂,框架里的那些配置对你来说就永远是黑盒。
8. 绕过"教科书顺序",聊聊初学JavaWeb真正应该养成的三个习惯
最后这一章,不写具体代码,想聊些更"软"的东西。我在带几个新人朋友入门时发现,技术细节能通过查资料补齐,但学习方法和调试思维上的差距,会直接影响学习效率。如果你正准备系统性学习JavaWeb,有这三个习惯越早养成越好。
8.1 日志不是报错后的救命稻草,而是开发过程中的仪表盘
我在前面已经提过两次"看日志"。这里想认真强调一下:很多初学者遇到报错第一反应是读红色异常信息,但Tomcat的控制台日志信息量大且混杂,新手往往只看最后一行"Exception"开头的内容,错过了更早的Caused by。这个问题在我每次排查时都很常见——真正的根因很少在最后一行,往往出现在某个"at com.example.xxx"的堆栈里,中间夹着"Servlet.service() threw exception"这类容器包装信息。
我自己处理日志的一个方法是:阅读异常信息时,从底部往上找第一个出现自己项目包名的行。那是离"你的代码出错"最近的地方。另外,从第一天起就给自己的每个Servlet配置slf4j日志(而不是System.out.println),虽然初期略麻烦,但长期收益巨大——到时候你就知道为什么所有框架都在用日志框架而不建议太多控制台输出了。
8.2 学会"最小化验证",而不是在烂代码上缝缝补补
我见过不少人写了个几百行的Servlet,一运行出错就开始一行行检查哪儿写错了,结果越看越糊涂。更高效的做法是把业务逻辑临时注释掉,先写一个只输出固定字符串的"最小版本",确认请求能到Servlet、响应能返回,再逐步把业务代码加回来。这个过程叫最小化验证,它能帮你快速锁定问题到底在链路哪一环:是请求没到?Servlet写错了?还是响应输出有问题?
每次碰到疑难杂症,我都默认遵循二分排查:先确认请求能进代码,再确认数据能取到,再确认逻辑没报错,最后看响应格式。按这个顺序,把问题按层切分,远比在一个大报错信息里猜答案高效。
8.3 建立"自己写个框架"的思维实验
学Servlet时会接触到Filter、Listener、Session、Cookie这些概念,如果只看它们各自的功能,容易记了就忘。我推荐一个思维实验:想象一下如果让你自己设计一个纯Java的Web框架(在Servlet基础上),你打算用哪个组件做权限拦截?哪个组件做统一编码?哪个组件做全局参数读取?
答案其实很直接:Filter做拦截,Listener做启动初始化,Session做状态保持。当你从"设计者"角度理解这些组件的分工,学到的就不仅仅是"Filter是过滤器"这句话,而是"为什么需要过滤器,以及过滤器放在哪里最合适"。这个思维实验对我后续学Spring MVC帮助巨大,因为框架里那些Interceptor、AOP概念,其实都隐藏着Servlet时代的老影子。
9. 写在第一篇日志的结尾:下一站学什么,以及给初学者的路线图
按计划,这篇只是系列的开胃菜,重点是把Web开发的全貌、JavaWeb的定位、Servlet的核心模型和常见环境配置讲清楚。下一篇我打算深入Servlet的会话跟踪技术,把Cookie、Session、Token这条线完整捋一遍,并写一个可运行的登录状态保持Demo。因为登录状态管理几乎是每个Web应用绕不开的硬骨头,也是从"Servlet"转向"框架"之前必须打牢的底子。
给你一个当前阶段我认为合理的JavaWeb学习路线图参考:
- HTML/CSS/JavaScript基础,会写简单页面、理解DOM操作和Ajax请求;
- Java SE基础,重点是面向对象、集合、IO、异常、多线程基础;
- MySQL基础,能写增删改查SQL,理解主键、外键和简单索引;
- 本篇内容——Servlet和Tomcat基础,理解HTTP、请求响应模型;
- JSP简单了解即可,重点放在会话管理和过滤器;
- Spring、Spring MVC、MyBatis三大框架;
- Spring Boot整合实践,做一个完整的CRUD项目;
- 之后视方向选择:微服务、前端框架深入学习、部署运维等。
这个路线不是死的,但如果你的目标是找一份Java开发工作,前七步几乎是所有人都会走的路径。区别在于,有的人每一步都是背概念糊弄过去,有的人像我一样重新回来逐层补课。这个系列,就是我补课的详细记录。
如果你正卡在某个具体环节,比如Tomcat启动失败、Maven依赖下载不下来、Servlet注解不生效,可以先翻翻我前面提到的排查方法——先看日志、再做最小化验证、再逐层拆。我在后续日记里也会把这些高频问题单独开篇详聊。下一篇文章见。