红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Java编程笔记
  4. 正文

Java编程笔记27:Servlet

2023年3月28日 1037点热度 0人点赞 0条评论

image-20221101145143893

图源:Fotor懒设计

在之前的文章Java编程笔记26:HTTP - 红茶的个人站点 (icexmoon.cn)中,我们学习了如何“纯手工”用Java编写一个基于TCP/IP通信的Web服务端应用,可以实现最基本的接收HTTP请求和返回HTTP响应。

但显然,这种方式只适合学习最基本的Web服务实现原理,并不适合真正用于商业开发。因为我们要花费大量时间去处理最基本的报文解析和请求响应等功能,这些功能显然是“轮子”,已经有大量成熟方案可以使用。

Servlet技术就是这其中一个比较古早的轮子,虽然现代基本不会用到它,但了解一下是相当有意义的。

原理

Servlet定义了一组用于实现Web应用的API,我们不需要自己实现基本的报文解析等操作,只要调用这些API实现具体的业务即可。运行的时候,只需要将我们的应用部署在支持Servlet API的Web Server上即可。

大概是这个样子:

image-20230328155320617

Servlet 应用

这里我们编写一个最简单的Servlet应用。

首先,需要创建一个mvn的工程,结构大概这样:

image-20230328160459042

需要注意的是,main目录下必须包含一个空白的webapp目录。

mvn的配置文件pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <groupId>org.example</groupId>
    <artifactId>my_serverlet</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
​
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>hello</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
        </plugins>
    </build>
​
</project>
  • 实际上Servlet API分为5之前的版本和5之后的版本,由不同的仓库提供,这里选择的是=5的版本。

  • 这里指定具体版本的maven-war-plugin插件是因为打包编译的时候会报Execution default-war of goal org.apache.maven.plugins:maven-war-plugin:2.2:war failed这样的错误,通过指定新版插件可以解决。

需要注意的是,这里<packaging>标签指定的打包类型不是通常的jar,而是war。war和jar的运行方式不同,前边说过,它要依赖支持Servlet API的Web Server运行。

还需要注意的是,Servlet API依赖jakarta.servlet-api的范围是<scope>provided</scope>,这意味着仅会在编译期使用,不会被打包到war中。这是因为部署的目标Web Server本身是包含相关依赖的,不需要重复包含。着同样意味着我们这里引用的Servlet API版本必须与目标Web Server支持的版本一致,具体来说,对于>=5的API,需要的Tomcat版本必须是10以上。

war是Java Web Application Archive的缩写。

具体的业务代码相当简单:

package cn.icexmoon.java.note.ch26;
​
// ...
@WebServlet(urlPatterns = "/")
public class ExpServerlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        if (name == null) {
            name = "none";
        }
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.print("<h1>This is a first serverlet app example.</h1>");
        String msg = String.format("<div>hello %s</div>", name);
        writer.print(msg);
        writer.flush();
    }
}

@WebServlet注解说明下边的类是一个Servlet应用,可以接收和响应HTTP报文。该注解的urlPatterns参数可以用于指定处理的路径。比如这里我们打包后的应用是hello.war,结合这里的@WebServlet(urlPatterns = "/"),那么会响应路径为localhost:xxxx/hello/这样的HTTP请求。

在Servlet应用中,承担HTTP Handler工作的类必须继承自HttpServlet,而具体的请求处理和响应工作要通过重写doGet和doPost方法的方式实现。

具体的读入请求参数和写入响应内容的方式与一般的Web编程类似,这里不再详细说明。

一切准备好后可以通过mvn package之类的命令来进行打包,并且得到hello.war这样的一个包。这样我们就可以准备运行这个示例应用了。

运行

Servlet应用必须依赖Web Server,这里选取的是Tomcat(具体的版本是10.0.23)。

安装好后可以在命令行下执行/bin/startup.bat来启动Tomcat。

如果有报错信息,窗口一闪就退出的情况,可以查看/logs目录下的日志。通常来说可能是没有正常退出导致进程占用端口之类的原因,可以通过杀掉进程或者重启电脑的方式解决。

正常启动后可以看到类似下面的信息:

28-Mar-2023 15:21:06.297 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["http-nio-8081"]
28-Mar-2023 15:21:06.309 信息 [main] org.apache.catalina.startup.Catalina.start [552]毫秒后服务器启动

如果输出的是乱码,可以参考(11条消息) 很详细的解决Tomcat乱码问题_Monkey_King_GL的博客-CSDN博客解决。

这里的http-nio-8081说明服务端默认监听的是8081端口,也就是说我们应当请求localhost:8081来访问Tomcat。image-20230328163246923

现在将hello.war包放到Tomcat的/webapps目录下。

此时字符中端会出现类似下边的日志信息:

28-Mar-2023 15:21:33.342 信息 [http-nio-8081-exec-5] org.apache.catalina.core.StandardContext.reload 已开始重新加载名为[/hello]的上下文
28-Mar-2023 15:21:33.367 信息 [http-nio-8081-exec-5] org.apache.catalina.core.StandardContext.reload 已完成重新加载名为/hello的上下文

这说明Tomcat会自动监视webapps下部署的war包,如果有新的war包添加,就会自动读取和部署Servlet应用。

实际上可Tomcat提供简单直观的方式管理和部署应用,只要点击首页右侧的Manager App即可进入:

image-20230328164021555

通过这个页面可以很容易地管理应用(比如在替换war包后重新加载应用)。

第一次进入页面需要输入用户名和密码,Tomcat默认是没有用户的,需要编辑/conf/tomcat-users.xml添加用户,比如:

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
           version="1.0">
 <!-- ... -->
 <role rolename="manager-gui"/>
 <user username="tomcat" password="tomcat" roles="manager-gui"/>
</tomcat-users>

这样就可以添加一个用户名为tomcat密码为tomcat且拥有manager-gui角色的用户,具体的角色权限可以参考官方文档。

如果一切都正常的话,此时尝试请求就可以看到想要的结果:

image-20230328164600449

OK,到这里就结束了,谢谢阅读。

本文中的完整示例工程文件可以通过java-notebook/ch27 (github.com)获取。

参考资料

  • Servlet入门 - 廖雪峰的官方网站 (liaoxuefeng.com)

  • (11条消息) 很详细的解决Tomcat乱码问题_Monkey_King_GL的博客-CSDN博客

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: servlet web
最后更新:2023年3月30日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号