刚开始学Java Web的时候,我有个疑问:我在IDEA里写了一大堆Java代码,浏览器怎么知道要调用我的哪个方法?
后来才明白,这中间有个叫Tomcat的"中间人"。浏览器发请求给Tomcat,Tomcat根据请求的地址,找到我写的对应的Java类,然后调用它。
这个能被Tomcat找到并调用的Java类,就是Servlet。
一、创建Servlet的三个步骤
创建一个Servlet需要三步:实现规范、重写方法、配置路径。
1.1 继承HttpServlet类
Servlet规范要求所有的Servlet都必须实现javax.servlet.Servlet接口。但直接实现接口太麻烦,需要实现五个方法。好在Tomcat提供了一个实现类HttpServlet,我们直接继承它就行。
HttpServlet已经帮我们实现了Servlet接口的大部分方法,我们只需要关注业务逻辑即可。
1.2 重写service方法
当浏览器访问Servlet时,Tomcat会自动调用service方法。所以我们在这个方法里写具体的处理逻辑。
java
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 设置响应类型和编码
resp.setContentType("text/html;charset=utf-8");
// 获取输出流,向浏览器写数据
PrintWriter out = resp.getWriter();
out.println("<h1>我的第一个Servlet</h1>");
out.println("<p>当前时间:" + new java.util.Date() + "</p>");
}
}
这里有两个关键对象:
-
HttpServletRequest:封装了浏览器的请求信息,包括参数、请求头、Cookie等
-
HttpServletResponse:封装了要返回给浏览器的响应信息
1.3 配置访问路径
写好了Servlet,还得告诉Tomcat这个Servlet对应哪个URL地址。有两种配置方式,现在主流用的是注解。在类上加上@WebServlet注解,指定路径:
java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
// ...
}
这个注解告诉Tomcat:当浏览器访问"/hello"路径时,就调用这个Servlet来处理。
路径必须以斜杠开头,而且在整个项目中必须唯一,不能有两个Servlet用同一个路径。
二、浏览器如何访问Servlet
配置完成后,把项目部署到Tomcat,启动服务器。假设项目名叫demo,那么这个Servlet的访问地址就是:
html
http://localhost:8080/demo/hello
这个URL拆开来看:
-
localhost:8080:Tomcat的地址和端口
-
demo:项目名(也叫上下文路径)
-
hello:注解里配置的Servlet路径
浏览器发送请求到这个地址,Tomcat收到后,根据/demo找到项目,再根据/hello找到对应的Servlet,最后调用它的service方法。
三、service、doGet和doPost的关系
刚才我们重写的是service方法。但在很多教程里,看到的是重写doGet和doPost。
查看HttpServlet的源码会发现,它本身已经实现了一个service方法,逻辑大致是:
java
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if ("GET".equals(method)) {
this.doGet(req, resp);
} else if ("POST".equals(method)) {
this.doPost(req, resp);
}
// 还有其他请求方式...
}
也就是说,service方法会根据请求方式(GET/POST等),自动调用对应的doXxx方法。
所以我们有两种写法:
写法一:直接重写service方法(用的多,省事儿)
java
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
// 不管GET还是POST,都走这里
}
这种写法最简单,适合不需要区分请求方式的场景。
写法二:分别重写doGet和doPost
java
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 处理GET请求
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// 处理POST请求
}
这种写法更规范,适合GET和POST要做不同处理的场景。
初学者可以先从重写service方法入手,代码更简洁。
四、@WebServlet注解的更多用法
@WebServlet除了指定路径,还有一些其他常用属性:
java
@WebServlet(
name = "HelloServlet", // Servlet名称,可选
urlPatterns = {"/hello", "/hi"}, // 可以配置多个访问路径
loadOnStartup = 1 // 是否在启动时加载
)
urlPatterns支持配置多个路径,访问其中任何一个都会进入这个Servlet。比如配了{"/hello", "/hi"},那么/hello和/hi都指向同一个Servlet。
loadOnStartup控制Servlet的加载时机。默认是第一次访问时才创建实例,如果设置loadOnStartup=1,Tomcat启动时就会创建这个Servlet。数字表示启动顺序,值越小优先级越高。
五、写一个简单的登录接口
把上面学的内容串起来,写一个处理登录请求的Servlet:
java
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 设置请求和响应的编码
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
// 获取浏览器传递的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 获取输出流
PrintWriter out = resp.getWriter();
// 验证用户名密码
if ("admin".equals(username) && "123456".equals(password)) {
out.println("<h1>登录成功,欢迎 " + username + "</h1>");
} else {
out.println("<h1>登录失败,用户名或密码错误</h1>");
}
}
}
部署后通过浏览器访问:
html
http://localhost:8080/demo/login?username=admin&password=123456
注意路径里的/login要和注解一致,参数名要和代码里的getParameter一致。
六、Servlet的生命周期
Servlet从创建到销毁会经历几个阶段。
java
@WebServlet("/life")
public class LifeServlet extends HttpServlet {
// 1. 构造方法:第一次访问时调用
public LifeServlet() {
System.out.println("构造方法执行");
}
// 2. init方法:构造完成后调用,只执行一次
@Override
public void init() {
System.out.println("init方法执行");
}
// 3. service方法:每次请求都会调用
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("service方法执行");
}
// 4. destroy方法:服务器关闭时调用
@Override
public void destroy() {
System.out.println("destroy方法执行");
}
}
第一次访问这个Servlet时,控制台会依次打印:构造方法、init方法、service方法。后续再次访问,只会打印service方法。关闭Tomcat时打印destroy方法。
整个生命周期是:构造 → init → service(反复执行) → destroy。
如果配置了loadOnStartup,构造和init会在Tomcat启动时就执行,而不是等到第一次访问。
七、常见问题提醒
- 注解里的路径一定要加斜杠,@WebServlet("/hello")是对的,@WebServlet("hello")会找不到。
- 路径在整个项目中必须唯一,不能有两个Servlet用同一个路径,否则Tomcat启动时会报错。
- 如果浏览器访问时报404,先检查三件事:注解路径写对了吗?URL里的项目名对吗?Tomcat启动时有报错吗?
- service方法里resp.getWriter()获取的输出流,写入的内容会直接显示在浏览器页面上。如果写的是HTML标签,浏览器会解析渲染。
八、总结
创建一个Servlet只需要三步:
-
继承HttpServlet类
-
重写service方法(或doGet/doPost)
-
用@WebServlet注解配置访问路径
浏览器访问时,Tomcat根据URL找到对应的Servlet,调用service方法。我们可以通过HttpServletRequest获取请求参数,通过HttpServletResponse返回响应数据。
以上是我对Servlet的快速入门理解,刚学的时候我也被各种概念绕晕,写下来梳理一遍,希望能帮到同样在路上的你。