ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Servlet零基础入门:吃透Java Web底层核心,Spring Boot学习事半功倍

Servlet零基础入门:吃透Java Web底层核心,Spring Boot学习事半功倍 Servlet 这东西很多零基础的朋友一听就觉得是上古技术觉得现在都用 Spring Boot 了谁还学 Servlet但我不这么看。Servlet 是 Java Web 的地基Spring MVC 和 Spring Boot 的请求处理核心本质上就是一个 Servlet你把 Servlet 搞明白了后面学框架就是降维打击。这篇教程我会从零开始手把手带你创建项目、写第一个 Servlet、配置 Tomcat 跑起来再深入到请求响应处理、生命周期、乱码和文件上传解析这些高频问题最后告诉你学完 Servlet 之后该怎么走。无论你是刚学完 Java 语法的小白还是想补基础的在职开发这篇文章都值得你跟着敲一遍。1. Servlet 是什么一个跑在服务器上的 Java 类1.1 一次 Web 请求的完整旅程先不看任何官方定义我们直接想一个场景你打开浏览器输入网址敲回车页面出来了。中间发生了什么浏览器把请求发给服务器服务器上某个程序处理了这个请求把结果返回给浏览器浏览器再把结果渲染成页面。在这个过程里那个“处理请求的程序”在 Java 世界里最常见的形态就是 Servlet。服务器不是直接把 index.html 扔回去那么简单。现在绝大多数网站都是动态的页面里的数据来自数据库来自用户登录状态来自实时计算。这时候服务器就需要一段程序来“算”出结果。Servlet 就是这段程序的标准载体。你不用关心浏览器和服务器之间怎么建立连接那些是 Tomcat 这类容器干的活你只需要写清楚收到请求之后要干什么。有意思的是现在前端圈都在聊 Astro、Next.js 这类静态生成框架觉得服务端渲染是旧时代产物。但只要一个应用涉及用户登录、表单提交、文件上传就绕不开动态请求处理而 Servlet 规范就是这套动态处理逻辑最原始的约定。框架换了一茬又一茬底层这套请求-响应模型没变过。1.2 Servlet 容器和 Servlet 的分工要理解 Servlet必须先分清两个角色Servlet 容器比如 Tomcat和 Servlet 本身。容器负责网络通信、解析 HTTP 请求、管理 Servlet 的生命周期Servlet 负责业务逻辑也就是拿到请求数据后怎么处理、怎么响应。打个比方Tomcat 像个餐厅大堂经理Servlet 是后厨厨师。客人点菜发起请求大堂经理把菜单HTTP 请求传到后厨厨师做好菜处理业务再由大堂经理把菜端给客人返回响应。你作为开发者的工作就是写好这道菜的做法也就是 Servlet 类里面 doGet、doPost 这些方法。至于大堂经理怎么传菜、怎么安排座位你不用操心。这个分工带来了一个关键好处你在本地写的 Servlet部署到任何遵循 Servlet 规范的容器上都能跑。Tomcat、Jetty、Undertow 随便换代码不用改。因为容器和你的代码之间隔着规范容器负责实现规范你的代码只依赖规范的 API。这就是为什么你现在学的 Servlet API放到十年前的老项目里照样能看懂。1.3 为什么 2025 年还要从 Servlet 学起我见过太多直接上手 Spring Boot 的新人遇到问题一头雾水。比如为什么控制器方法返回字符串会被当作用户名为什么过滤器不生效为什么请求转发报 404这些问题背后的答案全都在 Servlet 规范里。Spring MVC 的核心组件 DispatcherServlet它的全名是 org.springframework.web.servlet.DispatcherServlet你听听后缀就是 Servlet。Tomcat 启动时把请求交给 DispatcherServlet再由它分发给各个 Controller 方法。如果你不懂 Servlet 的生命周期你就理解不了为什么 DispatcherServlet 是单例的为什么线程不安全的问题要自己注意如果你不懂 Servlet 的请求对象你就理解不了 Spring MVC 的 HandlerMethodArgumentResolver 在干什么。所以我的建议非常明确零基础可以跳过 JSP但一定要先学 Servlet 和 Filter。这个基础打牢了后面学 Spring Boot 会顺利得多。2. 开发环境与标准项目结构2.1 工具清单和环境版本动手之前先把环境准备好。我的推荐组合如下都是目前最主流的版本用着最稳。JDK8 或 11 都行。如果你完全没装过直接装 JDK 11。Servlet 4.0 规范在 JDK 8 以上都能跑。Tomcat9.0.x。对应 Servlet 4.0 规范也是 Java EE 8 时代最常见的容器。Tomcat 10 以后把包名改成了 jakarta.servlet很多老教程不适用零基础建议先用 Tomcat 9。IDEA社区版就够用不用花钱。Maven3.6 以上用来管理依赖和打包。这里提醒一下很多人下载 Tomcat 之后直接双击 startup.bat 启动然后发现端口被占用或者启动闪退。先检查 JAVA_HOME 环境变量有没有配好在命令行执行 echo %JAVA_HOME% 能打印出 JDK 路径才能继续。Tomcat 启动后默认监听 8080 端口如果被别的服务占了改 conf/server.xml 里的 port 配置就行。2.2 创建一个标准 Web 项目目录Servlet 项目虽然能用 IDEA 直接建但零基础我建议先手动导出标准目录结构这样你对项目布局有概念。一个标准的 Java Web 项目长这样my-servlet-demo/ ├── pom.xml └── src/ └── main/ ├── java/ │ └── com/example/servlet/ │ └── HelloServlet.java ├── resources/ └── webapp/ ├── WEB-INF/ │ └── web.xml └── index.jsp最关键的是 webapp/WEB-INF 目录。WEB-INF 是 Web 应用的私有目录外部浏览器直接访问不到里面的文件所有受保护的配置和类文件都放这里。web.xml 是 Web 应用的部署描述符早期版本必须在里面配置 Servlet 映射Servlet 3.0 之后可以用注解替代但我建议你至少把它手工配置过一次知道原理再偷懒。pom.xml 里只需要一个 Servlet API 依赖并且 scope 必须是 provided因为 Tomcat 自带 Servlet 实现打包的时候不需要打进去project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdservlet-demo/artifactId version1.0-SNAPSHOT/version packagingwar/packaging properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency /dependencies /project注意 packaging 类型是 war不是 jar。Web 应用需要打成 WAR 包扔进 Tomcat 的 webapps 目录Tomcat 会自动解压部署。你要是用 IDEA 的 Artifacts 功能也是生成一个 exploded war 目录来部署。2.3 IDE 里配置 Tomcat用 IDEA 的话配置 Tomcat 分三步。第一步Run 菜单里选 Edit Configurations点加号找到 Tomcat Server选 Local。第二步在 Application server 里点 Configure选择你本地 Tomcat 解压目录。第三步Deployment 选项卡里添加 Artifact选 war exploded。这样启动后会直接编译并部署到 Tomcat 的 webapps 目录浏览器打开 http://localhost:8080/项目路径/ 就能访问。这里面最容易出问题的是 Application server 配置识别不到。IDEA 要求 Tomcat 目录下有 bin、conf、lib 这些标准子目录你下载错了二进制包比如下载了 tar.gz 源码包就会识别失败。Tomcat 官网下载页要选 Binary Distributions 下的 Core 包别选错了。3. 从零写出并跑通第一个 Servlet3.1 编写最简单的 Servlet 类环境准备好了我带你写第一个 Servlet。这个类继承 HttpServlet重写 doGet 方法当浏览器用 GET 请求访问它时输出一行 HTML。package com.example.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().write(h1Hello Servlet, 我的第一个动态页面/h1); resp.getWriter().write(p当前时间 System.currentTimeMillis() /p); } }这段代码信息量不少先拆开看。HttpServlet 是 Servlet 规范提供的一个抽象类默认实现了 service 方法service 会根据请求方法自动分派GET 请求调 doGetPOST 请求调 doPostPUT 请求调 doPut。这也是为什么你只需要重写 doGet 就行不用自己写 if 判断请求类型。resp.getWriter() 拿到一个 PrintWriter往里面写的内容会作为响应体发回浏览器。setContentType 很关键告诉浏览器这段内容是什么类型、用什么编码。你不写这句话浏览器可能把中文显示成乱码或者干脆当作纯文本显示标签。charsetUTF-8 是必须的下面的乱码章节我还会细说。3.2 用 web.xml 配置访问路径有了 Servlet 类还不够Tomcat 不知道什么 URL 能访问到它需要配置一条映射关系。在 web.xml 里加这个?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 display-nameservlet-demo/display-name servlet servlet-namehello/servlet-name servlet-classcom.example.servlet.HelloServlet/servlet-class /servlet servlet-mapping servlet-namehello/servlet-name url-pattern/hello/url-pattern /servlet-mapping /web-app配置分两段。第一段 声明一个 Servlet 实例给它起个名字 hello告诉容器用哪个类。第二段 把名字和 URL 地址关联起来。你访问 http://localhost:8080/你的应用名/hello 时Tomcat 会找到名为 hello 的 Servlet实例化它然后调用 service 方法。我见过不少新手改了 web.xml 发现不起作用第一反应是重启没用其实是根本没有重新构建部署。修改 web.xml 后必须重新部署IDEA 里点一下 Redeploy或者干脆重启 Tomcat。不是改了等一分钟它就自动生效Tomcat 开发模式下可能会热加载 class但 web.xml 这种部署描述符的改动在很多配置下不会被热加载。3.3 部署到 Tomcat 并启动验证部署方式有两种我用最直观的 IDEA 方式演示。启动 Tomcat 后IDEA 会自动把项目打成 exploded war 目录放到 Tomcat 的 webapps 下并创建 context 路径。你在浏览器输入 http://localhost:8080/servlet-demo_war_exploded/hello如果看到“Hello Servlet, 我的第一个动态页面”说明整个链路已经通了。这里教你看懂 Tomcat 的日志这是以后排查问题的基本功。启动日志里会有这么一行INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [servlet-demo]如果出现红色的 SEVERE 或者 ERROR说明部署失败。最常见的错误是 Servlet 类找不到这种往往是你配置 web.xml 时类名写错了包名和类名不匹配。日志里会直接报 ClassNotFoundException顺着错误往上找把全限定类名复制出来和自己的实际类路径对比一下基本一分钟能定位。3.4 用 WebServlet 注解替代 XMLServlet 3.0 之后官方提供了注解方式省去 web.xml 那段繁琐配置。把 HelloServlet 类上加上 WebServlet 注解package com.example.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; WebServlet(/hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().write(h1Hello Servlet, 我的第一个动态页面/h1); } }然后你完全可以把 web.xml 里的 servlet 和 servlet-mapping 两段删掉只保留一个空的 标签。Tomcat 启动时会扫描 class 文件上的注解自动完成注册。我建议零基础把两种方式都试一遍XML 方式了解原理注解方式实际使用。真实项目里 Spring Boot 连 web.xml 都省了但对请求路径的理解还是这套。WebServlet 注解还支持配置多个路径比如 WebServlet({/hello, /hello2})还可以用通配符 WebServlet(/api/*)这些以后用到再查手册就行。4. 请求与响应处理的核心细节4.1 HttpServletRequest拿到请求里的数据Servlet 的第二个重要对象是 HttpServletRequest它封装了浏览器发来的所有数据。最常见的几个方法我直接整理成一个表格方法作用示例getParameter(String name)获取请求参数GET 的 query 参数或 POST 的表单参数req.getParameter(username)getParameterValues(String name)获取同名多值参数比如 checkboxreq.getParameterValues(hobby)getMethod()获取请求方法GET、POST、PUT 等req.getMethod()getRequestURI()获取请求地址中的路径部分不含域名req.getRequestURI()getHeader(String name)获取请求头比如 User-Agentreq.getHeader(User-Agent)setCharacterEncoding(String env)设置请求体的字符编码解决 POST 乱码req.setCharacterEncoding(UTF-8)举个例子浏览器请求 /hello?usernamezhangsanhobbyreadinghobbyswimmingServlet 里这样拿数据String username req.getParameter(username); String[] hobbies req.getParameterValues(hobby); String method req.getMethod();你会发现 getParameter 既支持 GET 也支持 POST 表单因为容器把两种情况都做了解析合并成了一个参数表。底层实现里 GET 参数放在 QueryString 里POST 表单在请求体里但开发者不用区分这也是 Servlet 规范替我们省掉的一大堆工作。4.2 POST 请求中文乱码一次给你说清楚乱码问题是 Servlet 新手遇到最多的坑而且困惑点在于为什么 GET 请求一般不乱码POST 请求却经常乱码先说原理。GET 请求的参数在 URL 里Tomcat 9 及以后的版本默认使用 UTF-8 解码 URL所以你 GET 传中文没问题。POST 请求的参数在请求体里Tomcat 默认用 ISO-8859-1 解码这编码压根没有中文字符所以必然乱码。解决办法是在读取任何参数之前调用req.setCharacterEncoding(UTF-8);这句话必须放在第一个 getParameter 调用之前必须在最前面。因为容器在第一次读取参数时就用当时的编码去解码了之后你再设置就晚了。同样的逻辑响应乱码是浏览器拿到字节流后不知道用什么编码解析所以需要设置resp.setContentType(text/html;charsetUTF-8);这两行代码一个管请求一个管响应是我每写一个 Servlet 必加的开头。还有一个偷懒但非常靠谱的方案就是写一个 Filter统一给所有请求设置编码后面我会在过滤器一节演示。总之Servlet 里出现中文乱码先检查这两行有没有加顺序对不对。4.3 HttpServletResponse写回 JSON 数据现在接口开发基本都是 JSON 交互Servlet 里返回 JSON 也非常简单。把响应类型改成 application/json然后用一个 JSON 工具库比如 Fastjson 或 Jackson把对象序列化后写出去Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); resp.setContentType(application/json;charsetUTF-8); MapString, Object data new HashMap(); data.put(code, 0); data.put(message, 成功); data.put(username, 张三); String json new ObjectMapper().writeValueAsString(data); resp.getWriter().write(json); }这里有个很容易犯的错误先调了 getWriter() 再调 setContentType。HTTP 协议里响应头必须在响应体之前发送一旦你开始往 getWriter() 里写数据响应头就固化下来了再想改 Content-Type 就晚了。所以正确顺序永远是先设置响应头相关配置再获取输出流。另外一个要点是 getWriter() 和 getOutputStream() 不能同时使用它们俩互斥。getWriter 输出字符getOutputStream 输出字节。正常场景用 getWriter 就够只有在需要输出文件内容、图片等二进制数据时才用 getOutputStream。你非要两个都调会直接抛 IllegalStateException。4.4 请求转发与重定向别再傻傻分不清请求转发forward和重定向sendRedirect是 Servlet 里最容易被混淆的两个操作我每年面试都会问能把它们讲清楚的人不多。请求转发的代码req.getRequestDispatcher(/hello).forward(req, resp);重定向的代码resp.sendRedirect(/hello);区别主要在三点。第一转发是服务器内部行为浏览器地址栏不会变不管转发到哪个路径地址栏始终是原始请求地址重定向是服务器告诉浏览器“你去另一个地址”浏览器地址栏会变成新地址。第二转发是一次请求请求可以带数据过去所以在转发的目标 Servlet 里可以通过 request 共享数据重定向是两次请求第一次请求的 req 对象在响应返回后就被销毁了想带数据只能用 session 或者直接在 URL 后面拼接参数。第三转发只能转发到当前应用内部路径重定向可以跳到任意外部 URL。实际开发里post 提交后避免表单重复提交用重定向回到列表页也就是经典的 PRG 模式Post-Redirect-Get而 Controller 内部页面跳转比如登录后跳到首页用转发更多。记住一条跨应用跳转、要刷新地址栏的场景用重定向内部请求联动、共享 request 数据的场景用转发。5. 生命周期、过滤器与高频问题排查5.1 Servlet 生命周期init、service、destroyServlet 的生命周期只有三个阶段让容器管理的开发者一般不用手动调用但你得理解它的时机。Servlet 实例创建后容器会调用 init(ServletConfig config) 方法这个方法在整个生命周期里只执行一次。你可以在这里做资源初始化比如加载数据库连接池、读取配置文件。默认情况下Servlet 是懒加载的也就是第一次被请求时才实例化和调用 init。如果想应用启动时就初始化web.xml 里配个 load-on-startup 数字就行数字小的先加载。之后每个请求进来容器都会调用 service 方法service 内部再根据 HTTP 方法分派到 doGet、doPost 等。service 方法可能会被多个线程同时调用因为容器会为每个请求分配一个线程。也就是说Servlet 是单例多线程的你在类里定义的实例变量多个请求之间共享存在线程安全问题。解决办法也很简单尽量不要在 Servlet 里定义可变的成员变量如果必须共享数据用局部变量或者加同步。最后应用关闭或 Servlet 被移除时容器调用 destroy 方法同样只执行一次用来释放资源。你可以在这里关掉连接池、写日志。理解生命周期对排查问题特别有帮助。比如你发现某个 Servlet 只有一个实例但它里有状态导致不同用户互相串数据这就是典型的线程安全问题。再比如你改了业务代码但 Tomcat 一直用旧逻辑往往是因为热部署没生效重启后才能重新初始化。5.2 过滤器 Filter请求进 Servlet 前的统一关卡Filter 是 Servlet 规范里和 Servlet 并列的另一大组件作用是在请求到达 Servlet 之前、响应离开 Servlet 之后执行统一的处理逻辑。官方叫法“过滤器”实际它既能拦截请求也能拦截响应。我写一个最常用的编码过滤器解决上面说的 POST 乱码问题package com.example.servlet; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); chain.doFilter(request, response); } }WebFilter(/*) 表示拦截所有请求。doFilter 方法里你可以在 chain.doFilter 之前写请求预处理比如登录校验、字符编码、日志记录在 chain.doFilter 之后写响应后处理比如给响应头统一加参数。注意 Filter 的执行顺序如果有多个 Filterweb.xml 里的配置顺序决定执行顺序注解方式无法保证顺序3.0 规范里没有定义注解顺序所以对顺序有要求的场景要用 web.xml 配置。Filter 和 Servlet 的关系是一条链路请求先经过 FilterFilter 里调用 chain.doFilter请求才继续往后传到 Servlet。你把 chain.doFilter 注释掉请求就断了Servlet 永远收不到请求。5.3 常见错误速查表我在带新人时统计过Servlet 阶段 90% 的错误是下面这几类整理成表格方便你对照排查。症状可能原因解决办法404提示请求路径不存在Servlet 类没启动映射路径写错或者应用根本没部署检查 web.xml/注解的 url-pattern 是否和访问路径一致看 Tomcat 启动日志确认部署成功404应用能访问但页面不存在访问的资源确实不存在比如路径里多了个空格检查 IDEA 的 Application context 配置通常访问路径要带项目名500 ClassNotFoundExceptionweb.xml 里 servlet-class 写错或类文件没编译到 WEB-INF/classes核对类的全限定名mvn clean 重新构建500 NoClassDefFoundError依赖缺失或依赖冲突检查 pom.xml 是否有对应依赖provided 作用域不会打包进 war500 但日志没明显错误业务代码运行时抛异常比如空指针查看 IDEARun 控制台完整堆栈找 Caused by 行405POST 不支持浏览器发了 POST但 Servlet 只重写了 doGet 没写 doPost重写 doPost 方法或者在 service 里统一处理中文全是问号数据库连接 URL 或 JSP 页面编码不对数据库连接字符串加 useUnicodetruecharacterEncodingutf8页面保持 UTF-8修改代码后没变化Tomcat 没有热加载或者浏览器缓存了旧页面清浏览器缓存强制刷新确认重新部署成功除了表格里的还有一个很常见的“灵异事件”启动后浏览器提示端口被占用。八成是之前 Tomcat 崩了没完全退出进程还占着 8080。Windows 下用 netstat -ano | findstr 8080 找到 PID再 taskkill /F /PID 进程号杀掉。Mac/Linux 用 lsof -i:8080 和 kill -9 搞定。5.4 文件上传解析失败的典型案例failed to parse multipart servlet request有一个问题在 Spring Boot 里很常见但根子上在 Servlet 容器就是上传文件时报feignclient failed to parse multipart servlet request; nested exception is ...。要理解这个问题先要知道 multipart 是什么。浏览器上传文件时表单的 enctype 必须设为 multipart/form-data请求体会把表单字段和文件内容按一段段边界boundary隔开。Servlet 容器需要解析这个复杂的请求体把文件内容提取出来。Spring Boot 里对应 spring.servlet.multipart 配置spring.servlet.multipart.max-file-size10MB spring.servlet.multipart.max-request-size20MB这个报错出现通常是下面几种情况。一是文件大小超过 max-file-size直接抛 MaxUploadSizeExceededException。二是 Feign 调用上传接口时Content-Type 的 boundary 没正确传递服务端解析 multipart 失败。三是请求体的格式不对比如前端没设置 enctype或者手动拼了 JSON 却用 multipart 的接口接收。排查这个问题的思路先看异常堆栈里具体是哪个细节是 SizeLimitExceededException 还是 IllegalStateException前者是文件超限后者是请求体解析失败。如果是 Feign 场景检查调用方编码确保用了 feign-form 这个库并且 RequestPart 注解的 name 要和 provider 接口的参数名一致。很多人容易漏掉这个问题以为格式对了就行但在 Feign 的封装里文件类型需要显式指定不是光传 MultipartFile 就完事。这个问题的底层还是 Servlet 规范里 MultipartConfig 的解析逻辑。Servlet 3.0 提供了 MultipartConfigElement容器自动帮你解析 multipart 请求你不用手动处理边界。Spring Boot 把这个逻辑封装成了 StandardServletMultipartResolver。你理解到这一层以后遇到类似日志就不会慌。6. 从 Servlet 到框架理解 Spring MVC 的底层6.1 DispatcherServlet本质还是一个 Servlet当你用 Spring Boot 开发时其实还是 Servlet 那一套只是被框架深深包装了。Spring MVC 的核心 DispatcherServlet就是一个特殊的 Servlet它被注册为处理所有 / 路径的请求然后在内部做一系列路由分发。我把这个流程讲清楚Tomcat 收到请求后找到 DispatcherServlet调用它的 service 方法。DispatcherServlet 内部做两件事第一根据请求路径找到对应的 HandlerMethod也就是你写的 Controller 方法第二通过 HandlerAdapter 调用这个 Controller 方法把方法的返回值处理成响应。所以你写的 Controller 方法本质上是被包装之后的 Servlet 业务逻辑片段。理解了 Servlet 再看 Spring MVC你会发现很多设计都是顺理成章的。HandlerInterceptor 的 preHandle 相当于 Filter只是粒度更细RequestParam 就是从 req.getParameter 包了一层RequestBody 是从 req.getInputStream 解析 JSON 得到的。框架没有发明新东西只是在 Servlet 之上做了更好的封装。6.2 Servlet 规范里还有哪些成员Servlet 规范除了 Servlet、Filter还有几个组件值得了解。Listener监听器可以监听 ServletContext、HttpSession、ServletRequest 的生命周期事件。HttpSession 是会话机制解决 HTTP 无状态问题session.setAttribute 存储用户登录状态底层实现和 Cookie 一脉相承。ServletContext 是应用全局上下文整个应用只有一个实例可以放全局配置。这些人你可能听说过但没细究其实都是 Servlet 规范的一部分。Session 在分布式场景下的问题比如多台服务器怎么共享登录状态就是从这个最基础的概念延伸出去的。我的建议是先把这些名词搞明白不用深入等真正遇到具体场景再回头研究。6.3 零基础学习路线建议如果这篇文章你看到了这里我觉得你有两种可能一种是照着操作已经跑通了另一种是还在犹豫要不要动手。无论哪种我给你一个清晰的下一步路线。第一步把第一个 Servlet 用两种方式web.xml 和注解都跑通感受请求响应的完整链路。第二步用 Servlet Filter Session 自己写一个最简陋的登录系统别用任何框架就靠 HttpServletRequest 和 HttpServletResponse。做完这个你对 Web 开发的基本功就算扎实了。第三步开始接触 Spring MVC你会发现之前学的所有东西都变成了框架配置里的概念学习曲线会平缓很多。第四步再学 Spring Boot把它当作自动配置的 Spring不要上来就抄注解。我对这条路线的信心来自我带过的新人。那些先在原生 Servlet 上磕过一遍的人后面问的问题质量都完全不同他们能快速定位是框架问题还是 HTTP 层面问题。反观直接上手 Spring Boot 的人遇到 404、乱码、请求转发失败这些问题经常折腾一下午也不知道源头在哪。我在实际写代码时有个习惯遇到框架里的报错会先问自己一句如果是纯 Servlet 会怎么处理这个问题经常能把复杂问题变成简单问题。Servlet 确实是老技术但老技术恰恰意味着它是经过时间沉淀的底层约定越底层的东西越值得花时间理解清楚。你把这个地基打牢了以后学任何 Java Web 框架都不会心虚。
返回列表