【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的快速入门理解,刚学的时候我也被各种概念绕晕,写下来梳理一遍,希望能帮到同样在路上的你。

相关推荐
小峰编程2 小时前
二进制安装Nginx——详细
linux·运维·服务器·nginx·云原生
刚入坑的新人编程2 小时前
Linux-cgdb
linux·运维·服务器
无限码农2 小时前
2.1 网络编程 异步网络库zvnet
服务器·网络·php
丿罗小黑2 小时前
【2026】Openclaw使用经验(阿里云服务器)
运维·服务器·chrome
在屏幕前出油2 小时前
02. FastAPI——路由
服务器·前端·后端·python·pycharm·fastapi
进击切图仔2 小时前
Linux 挂载操作
linux·运维·服务器
炽天使3282 小时前
龙虾尝鲜记(4)——Ubuntu 笔记本无头服务器全配置
linux·服务器·ubuntu
dashizhi20152 小时前
服务器如何记录共享文件访问日志、查看用户访问共享文件的行为日志?
运维·服务器
tongxh4232 小时前
自己编译RustDesk,并将自建ID服务器和key信息写入客户端
运维·服务器