图源:
在之前的文章中,我们学习了如何“纯手工”用Java编写一个基于TCP/IP通信的Web服务端应用,可以实现最基本的接收HTTP请求和返回HTTP响应。
Servlet技术就是这其中一个比较古早的轮子,虽然现代基本不会用到它,但了解一下是相当有意义的。
原理
Servlet定义了一组用于实现Web应用的API,我们不需要自己实现基本的报文解析等操作,只要调用这些API实现具体的业务即可。运行的时候,只需要将我们的应用部署在支持Servlet API的Web Server上即可。
大概是这个样子:
Servlet 应用
这里我们编写一个最简单的Servlet应用。
首先,需要创建一个mvn的工程,结构大概这样:
需要注意的是,main
目录下必须包含一个空白的webapp
目录。
mvn的配置文件pom.xml
如下:
<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;
// ...
urlPatterns = "/")
(public class ExpServerlet extends HttpServlet {
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]毫秒后服务器启动
如果输出的是乱码,可以参考解决。
这里的http-nio-8081
说明服务端默认监听的是8081
端口,也就是说我们应当请求localhost:8081
来访问Tomcat。
现在将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
即可进入:
通过这个页面可以很容易地管理应用(比如在替换war
包后重新加载应用)。
第一次进入页面需要输入用户名和密码,Tomcat默认是没有用户的,需要编辑
/conf/tomcat-users.xml
添加用户,比如:<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
角色的用户,具体的角色权限可以参考官方文档。
如果一切都正常的话,此时尝试请求就可以看到想要的结果:
OK,到这里就结束了,谢谢阅读。
本文中的完整示例工程文件可以通过获取。
文章评论