Servlet 实例
从零写一个可运行的 Servlet:注解映射、响应输出、部署与访问。
Key Points用 @WebServlet 声明 URL,重写 doGet 写响应,打成 war 放进容器的 webapps 目录即可访问。
完整代码
Java
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(name = "hello", urlPatterns = {"/hello", "/hi"})
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
String name = req.getParameter("name");
if (name == null || name.isBlank()) {
name = "World";
}
try (PrintWriter out = resp.getWriter()) {
out.println("<!DOCTYPE html>");
out.println("<html><body>");
out.println("<h1>Hello, " + escape(name) + "</h1>");
out.println("</body></html>");
}
}
private static String escape(String s) {
return s.replace("&", "&").replace("<", "<").replace(">", ">");
}
}urlPatterns is an array, so one servlet can serve several URLs. name is just an internal identifier and may be omitted.
等价于 web.xml
XML
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="6.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>Annotation or web.xml — pick one. web.xml lets you change mappings without recompiling and overrides annotations when both exist.
访问与响应
Bash
curl -s "http://localhost:8080/myapp/hello?name=Ada"Output
<!DOCTYPE html>
<html><body>
<h1>Hello, Ada</h1>
</body></html>URL 映射的三种写法
| Pattern | Matches |
|---|---|
| /hello | 只有 /hello |
| /api/* | /api/users、/api/orders/1 |
| *.do | 任意以 .do 结尾的路径 |
/ is the default servlet mapping and swallows everything unmatched, static assets included — get it wrong and every CSS and image 404s. Use /* to wrap static resources, / to handle only unmatched requests; the two are not the same.完整代码
Java
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(name = "hello", urlPatterns = {"/hello", "/hi"})
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
String name = req.getParameter("name");
if (name == null || name.isBlank()) {
name = "World";
}
try (PrintWriter out = resp.getWriter()) {
out.println("<!DOCTYPE html>");
out.println("<html><body>");
out.println("<h1>Hello, " + escape(name) + "</h1>");
out.println("</body></html>");
}
}
private static String escape(String s) {
return s.replace("&", "&").replace("<", "<").replace(">", ">");
}
}urlPatterns 是数组,一个 Servlet 可以绑多个 URL。name 只是容器内部标识,可以省略。
等价于 web.xml
XML
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="6.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>注解和 web.xml 二选一即可。web.xml 的优点是不用重新编译就能改映射,且能覆盖注解;两者冲突时 web.xml 生效。
访问与响应
Bash
curl -s "http://localhost:8080/myapp/hello?name=Ada"Output
<!DOCTYPE html>
<html><body>
<h1>Hello, Ada</h1>
</body></html>URL 映射的三种写法
| 写法 | 匹配 |
|---|---|
| 精确匹配 | 只有 /hello |
| 前缀匹配 | /api/users、/api/orders/1 |
| 扩展名匹配 | 任意以 .do 结尾的路径 |
/ 是默认 Servlet 的映射,会接管所有未被匹配的请求,包括静态资源——配错会让 CSS、图片全部 404。想覆盖静态资源用 /*,想只处理未命中用 /,两者语义不同。