【Servlet】浏览器与服务器的交互

刚开始学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只需要三步:

  1. 继承HttpServlet类

  2. 重写service方法(或doGet/doPost)

  3. 用@WebServlet注解配置访问路径

浏览器访问时,Tomcat根据URL找到对应的Servlet,调用service方法。我们可以通过HttpServletRequest获取请求参数,通过HttpServletResponse返回响应数据。


以上是我对Servlet的快速入门理解,刚学的时候我也被各种概念绕晕,写下来梳理一遍,希望能帮到同样在路上的你。

相关推荐
WiChP3 小时前
【V0.1B5】从零开始的2D游戏引擎开发之路
java·服务器·数据库
Cx330❀5 小时前
一文吃透Linux System V共享内存:原理+实操+避坑指南
大数据·linux·运维·服务器·人工智能
花千树-0105 小时前
兼容 ThreadLocal 的用户上下文透传方案:WebFlux 项目改造实践
java·spring boot·servlet·jetty
IMPYLH6 小时前
Linux 的 false 命令
linux·运维·服务器·bash
小江的记录本6 小时前
【Linux】《Linux常用命令汇总表》
linux·运维·服务器·前端·windows·后端·macos
一匹电信狗7 小时前
【Linux我做主】进程程序替换和exec函数族
linux·运维·服务器·c++·ubuntu·小程序·开源
RisunJan7 小时前
Linux命令-mysqlimport(为MySQL服务器用命令行方式导入数据)
linux·服务器·mysql
Saniffer_SH8 小时前
【每日一题】一台可编程的PCIe 6.0主机 + 一套自动化CTS验证平台 + 一个轻量级链路分析系统
运维·服务器·测试工具·fpga开发·自动化·计算机外设·硬件架构
吕司8 小时前
Linux线程的概念
linux·运维·服务器
江西省遂川县常驻深圳大使8 小时前
openclaw.json配置示例
服务器·json·openclaw