30 KiB
title: HTTP, Servlet, Tomcat author: lensfrex cover: https://oss-img.ciduid.top/blog/covers/topimage-xb3-pc.jpg date: 2023-05-13 15:50:00
学习Servlet和Tomcat
前置任务:了解学习HTTP
如果你选了网络工程导论这门课,并且已经了解了HTTP相关的知识,则可以跳过本节,直接开主线任务:传送点,但是也可以重新开始学习这部分的内容。
想了解更多:HTTP | MDN
HTTP介绍
超文本传输协议(HyperText Transfer Protocol,HTTP)是一个应用层协议。所谓协议,就是数据传输格式的一个约定。
HTTP最初设计是用来传输html页面这种纯文本数据的,但是实际上只要声明好header,传输什么都是可以的,图片,文件等等,或者也可以摇身一变,变成另一个协议(grpc, 基于http2)来使用,总之,http其实是非常灵活的。
HTTP消息
一个规范的,完整的HTTP消息结构是这样的:
请求消息(GET方法):
地址:https://github.com
GET / HTTP/1.1
Host: github.com
authority: github.com
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35
content-length: 0
请求消息(POST方法,带请求体):
地址:http://bkjx.wust.edu.cn/Logon.do?method=logon
POST /Logon.do?method=logon HTTP/1.1
Host: bkjx.wust.edu.cn
Proxy-Connection: Keep-Alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Cookie: bzb_njw=303511D07EF501147333F6B099D16CB9; SERVERID=122
Origin: http://bkjx.wust.edu.cn
Pragma: no-cache
Referer: http://bkjx.wust.edu.cn/
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35
Content-Length: 94
userAccount=&userPassWorld=&encoded=dMcs2P8fNag0n1d03f0UVgu02^%^257F^%^25QF^%^25pcs0Ygd4fbgHs69
响应消息(header内容精简过):
HTTP/1.1 200 OK
Server: GitHub.com
Date: Sat, 13 May 2023 02:59:34 GMT
Content-Type: text/html; charset=utf-8
Vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Language, Accept-Encoding, Accept, X-Requested-With
content-language: en-US
ETag: W/"bfeda72459b363617514a4f13882cece"
Set-Cookie: _octo=GH1.1.496438640.1683116779; Path=/; Domain=github.com; Expires=Mon, 13 May 2024 02:59:39 GMT; Secure; SameSite=Lax
Accept-Ranges: bytes
Transfer-Encoding: chunked
<!DOCTYPE html>
<html lang="en" data-a11y-animated-images="system">
<head>
<meta charset="utf-8">
<link rel="dns-prefetch" href="https://github.githubassets.com">
...(还有一堆的网页内容)
请求和响应的消息体长得还挺像的,估计大家都能看出来了。
第一行 -『起始行』与『状态行』
http请求消息中,第一行为『起始行』:
GET / HTTP/1.1
GET /background.png HTTP/1.0
POST /Logon.do?method=logon HTTP/1.1
起始行分为三个部分,分别定义了请求方法,请求目标和当前请求使用的http协议版本
第一个部分为『请求方法』,一般有GET, POST, OPTIONS, PUT等好多种,但是实际使用几乎就只有GET和POST两种。
随后紧接着的部分是『请求目标』,在普通的http请求中也可以简单理解为请求路径,也就是咱们看到的浏览器地址后面的部分(xxx.com/abcd/efg中的/abcd/efg,bkjx.wust.edu.cn/Logon.do?method=logon中的/Logon.do?method=logon部分)
在部分场合下,请求目标有时也是一个绝对路径的URL,如当使用HTTP代理访问网站时,浏览器向代理服务器(如软件)发送的数据也是一个http请求,只不过真实的请求放在了body部分,代理程序只负责原样发送数据:CONNECT google.com:443 HTTP/1.1
这里就抄一下MDN的介绍:
- 一个绝对路径,末尾跟上一个 '?' 和查询字符串。这是最常见的形式,称为原始形式(origin form),被 GET、POST、HEAD 和 OPTIONS 方法所使用。
- POST / HTTP/1.1
- GET /background.png HTTP/1.0
- HEAD /test.html?query=alibaba HTTP/1.1
- OPTIONS /anypage.html HTTP/1.0
- 一个完整的 URL,被称为绝对形式(absolute form),主要在使用 GET 方法连接到代理时使用。GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1
- 由域名和可选端口(以 ':' 为前缀)组成的 URL 的 authority 部分,称为 authority form。仅在使用 CONNECT 建立 HTTP 隧道时才使用。CONNECT developer.mozilla.org:80 HTTP/1.1
- 星号形式(asterisk form),一个简单的星号('*'),配合 OPTIONS 方法使用,代表整个服务器。OPTIONS * HTTP/1.1
最后的一部分为『HTTP版本』,声明了这次请求使用的http版本。目前常用的版本有HTTP/1.1
,HTTP/2
,HTTP/3
。
另外,和其他HTTP版本不同的是,HTTP/3版本是在基于UDP的QUIC协议之上实现传输的,QUIC是与TCP和UDP等同级(传输层)的协议,而其他的HTTP版本都是在TCP协议上实现的,
对于响应消息,第一行为『状态行』。
HTTP/1.1 200 OK
HTTP/1.1 404 Not Found
HTTP/1.1 500 Internal Server Error
(心 脏 骤 停)
也是三个部分。
响应的状态行就挺简单的了。
第一个就不用说了。
第二个则为『状态码』,就是咱们常见的404, 200这些,表明了服务端处理请求的结果状态。
第三个部分则为『状态文本』,其实就是状态码的描述。
有哪些状态码,可以自行了解了解,最好记得一些常用的状态码的含义,要是乱抛状态码给前端有的是一顿毒打(
当然,在许多场合中,业务状态(成功,认证错误,参数错误等等)一般并不通过状态码表示,而是200响应,在响应消息体里边体现错误;但是也有状态码和消息体都体现的情况。具体还是要跟前端和客户端的小伙伴商量约定好规范和文档,按照团队的规范和文档行事,能少很多不必要的麻烦。
『标头』(Header)
请求和响应的消息体过了第一行之后,就到了『标头』(header)部分了,一般咱们把这玩意叫『请求头』和『响应头』。
http消息的header实际上就是一个键值对,格式为Key: Value
,一行一个
需要注意的是,这里的key是不分大小写的,例如Host
,host
,HOST
,hOST
都是同一个东西。
同一个key的Header也可以有多个值,因此,在各种http库中,获取到的header值都是一个List。
比如:
Set-Cookie: logged_in=no; Path=/; Domain=github.com; Expires=Mon, 13 May 2024 02:59:39 GMT; HttpOnly; Secure; SameSite=Lax
Set-Cookie: access_token=abcdefg; Path=/; Domain=github.com; Expires=Mon, 13 May 2024 02:59:39 GMT; Secure; SameSite=Lax
虽然如此,但是除非有必要(例如上面的例子中给客户端返回Cookie),在响应或者请求的时候最好不要这么干,即使可以这么干,否则在对方处理的时候会很麻烦。
一般来说,咱们会经常见到这些请求头或者响应头:
Host
: 请求的目标主机,通常是在同一个ip和端口上有多个不同的服务时,提供给nginx或者apache等类似的其他的反代网关判断究竟应该执行哪个配置块时使用的。User-Agent
: 表明请求客户端信息,一般格式为程序名/版本
,当然,也可以添加其他额外的信息,如firefox的ua:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Content-Type
: 请求/响应传输数据的类型,即下一节中body部分数据的格式。按照规范,这个字段的值应该使用MIME type格式,这里是一些常用的MIME类型。Accept
: 能接受的数据格式,一般为用逗号,
分隔开的若干个MIME type格式Accept-Encoding
: 指定能够接受的响应数据编码,和Accept
不同的是,Accept
指定的是body部分的数据格式,但是这里指定的是整个响应的数据编码,通常是压缩算法,如gzip, deflate, br
,设置了这个值后,如果服务端支持,响应的时候会对整个HTTP消息进行相应的压缩后,再返回响应给客户端,客户端需要进行解压解码后再进行处理。一般http框架和web框架都能自动进行自动的处理。Referer
: 说明请求是从哪里来的Cookie
: 向服务器请求时附带的一些信息,就是咱们常说的cookie,浏览器在请求的时候如果有相对应的Cookie,会自动附带上这些Cookie进行请求。Set-Cookie
: 服务端响应回来的Cookie,浏览器看到这个header之后就会根据这个header的值去保存cookie到浏览器里边。下次请求cookie对应规则的地址时会自动附带这些信息,即上一条中的Cookie字段。
Cookie一般用于用户的识别和身份认证以及一些额外信息的保存。了解更多关于Cookie的东西
实际上,很多header值只是作为一个参考,作为后端开发,不应该过于信赖前端/客户端发来的数据,后端程序必须要对前端/客户端做充足的数据校验,如Content-Type
,前端/客户端可以在请求的时候在Content-Type
字段里说他发了image/png
图片,但是可能实际上发来的数据是一个程序,如果不做任何校验直接保存到服务器上,那可就麻烦大了。
一般来说,很多http请求和web服务框架都能帮咱们写好这些通用的请求头和响应头,不用咱们过于操心,当然,如果有需求更改的话也能更改好。
这里对header的作用描述不一定十分的准确,可以查看相关的文档了解详细详情:HTTP 标头(header),Ctrl+F直接一搜就到
『主体』(body)
在前面的header定义完之后,需要各一行空行,才到http消息中的『主体』部分。
这个body部分就是这个请求真正想要传输的数据。
这部分可以是纯文本,也可以是二进制,都行,只要Content-Type说好了就行,没说的话,一般也行,但是得看后端处不处理了,一些后端框架不会帮你自动判断Body格式,或者不同的Content-Type处理的方式不同,如果请求的时候没有设置对,可能会有些麻烦事。
另外,上面的起始行/状态行和header行以及body和上面几行中间间隔的空行,和windows一样,使用的换行符都为CRLF,也就是
\r\n
,而不是linux/unix常用的\n
。当然,这个知道就好了,库会帮咱们处理好的。
HTTPS
HTTPS就是加密后的HTTP,'S'就是"Secure"。一般使用TLS来加密。原始的http消息经过全部加密后,再进行传输。只要中间偷听的人拿不到密钥,就不可能知道除了请求的ip以外任何的http信息。
关于现代TLS加密的机制,可以去看看这篇文章
一般约定http跑在80端口上,而https跑在443端口上,但是也可以用其他端口。
我的建议是,https能上就上。
主线任务:Servlet,Tomcat
Servlet,介绍
在开始Tomcat的学习之前,咱们先来了解Servlet。
很多教程里都有讲过,就是运行在服务端的applet,也就是『Server Applet』。
说实话,当我听到“Applet”的时候...印象里貌似已经是上世纪早已失传的老东西了...但是其实现在咱们说的的“Server Applet”和那个客户端的“Applet”不是同一个东西,虽然都是当年就有的东西。
Servlet实际上就是一个接口。
咱们先来点开Servlet(清掉了注释):
package javax.servlet;
import java.io.IOException;
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
是的,整个Servlet接口就这些代码。
那Servlet是干啥的?
大伙想想接口(interface)是用来干什么的?当然是一种规范约定。Servlet就是一种规范,所有实现Servlet的类,也就是处理网络请求的类要做什么。你要是不想按照Servlet来写,也是可以的,但是会少了很多库和框架的支持,很多东西就要你手动实现了。
初始化init(ServletConfig config)
该做什么,接收到请求service(ServletRequest req, ServletResponse res)
该做什么,服务器要退出了destroy()
又该做什么,这些Servlet都不负责帮你实现,都需要你自己去实现。
这里的Servlet只是非常底层的Servlet定义,实际上,咱们学Servlet,其实就是学HttpServlet,学HttpServlet来实现一些非常简单的Web响应处理;咱们说的Servlet,其实就是实现了HttpServlet的自己写的类。
HttpServlet根据Http对最原始的Servlet又封装了一层,也就是专门针对Http的Servlet。可以看到,相比于原来的几个方法,多了很多和http处理相关的方法。
HttpServlet最核心的方法就是doGet(HttpServletRequest req, HttpServletResponse resp)
, doPost(HttpServletRequest req, HttpServletResponse resp)
等等这种doXxx
的方法,实际上就是对应请求中GET
, POST
等等请求的处理,当服务接受到这些请求的时候,就进入对应的方法处理,从req读请求,再向resp写响应。
恭喜你,当你接触到这里的时候,就已经到了开始入门写Web程序的阶段了,你现在在做的事就是十几年前的服务端程序员在干的事了,当年web服务还不复杂的时候,很多时候就是拿Servlet写页面渲染来实现web服务。
当然,只是实现了Servlet并不代表就是一个能跑的服务端程序了。是,我们是写了Servlet,但是Socket那些IO相关的东西咱还没有呢。
Servlet并不负责和客户端直接沟通,也就是说,那些Socket侦听8080啥的底层IO处理并不是在Servlet里面,实际的连接处理那是『Tomcat』那家伙来负责的。
Tomcat,介绍
Tomcat是一个『Servlet容器』,也就是说,这玩意是个罐子,用来装一个个的Servlet程序。
上文说到过,咱们虽然写了Servlet,但是没人负责和客户端建立连接沟通,所以,这个事就是Tomcat来做的了。
要知道,解析原始Http消息是一件非常麻烦的事情,光是Http标准那几百页有的是你看的了,你要干吗?反正我是不会自己干的,更何况还有各种IO相关的脏活累活要干。但是咱们的大好人——Tomcat,这些都让它来负责了。
Tomcat侦听着端口,客户端向我们这边发来了一个请求,Tomcat对客户端发来的Http请求进行解析,解析出请求路径,方法,header之类的信息,根据这些信息,把请求小心翼翼地包装好,封装成HttpServlettRequest,交给对应的Servlet去处理,然后Servlet处理完之后就返回HttpServletResponse给Tomcat,Tomcat就把这个响应又写成http消息,交给客户端。一个通过Servlet实现的服务端程序处理一个请求地过程就这么完成了。
Servlet,开干!
说了这么多,估计大家手已经开始痒了。那就咱们现在就来开始写几个简单的HttpServlet玩玩!
梦的开始,Hello World
计算机最经典的一句名言,莫过于那一声响彻云霄的 『Hello World!』
现在,就来写个Servlet,当用浏览器访问的时候,能返回文本“Hello World!”
咱先来建一个简单的工程,什么框架都不用加,这样就好了嗼
很多JDK的发行版都没有带Servlet,因此如果写代码的时候没有Servlet和HttpServlet类,需要自己手动在Maven中引入依赖:
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
注意,上面引用的是jakarta.servlet
,因为19年oracle把javax送了出去,但是又不准继续使用javax这个名字,所以jdk11以后的版本用的都是jakarta作为包名,因此需要将名称改成jakarta。
也就是说,如果你还在用java 11以下的jdk,需要引入javax.servlet
的servlet接口。
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
这些完事之后,就可以开撸了。
-
继承HttpServlet类 前文说过,要实现Servlet,就需要实现相应的方法。但是咱不可能又要从头写起,因此咱们继承的是HttpServlet类,而不是直接实现Servlet接口。
import jakarta.servlet.http.HttpServlet; public class HelloWorld extends HttpServlet { }
这个时候,就可以去重写HttpServlet相应的
doXxx(HttpServletRequest req, HttpServletResponse resp)
方法来处理不同的Http请求了。 -
重写
doXxx(HttpServletRequest req, HttpServletResponse resp)
idea每日小技巧🥰
简单,idea里边直接
Alt+Insert
,选择Override Methods
可以看到,有很多可以重写的方法:
咱们这里选择
doGet()
这项就好了。选好之后,嘣,idea就给我们生成好了重写方法了。
记得删除原来的
super.doGet(req, resp);
我就不删,咋的 可能会导致服务端直接返回
400
或者405
方法不支持错误给客户端,按住ctrl点开super的doGet
就能看到了:往
doGet
方法添加(粘贴)这些内容:PrintWriter writer = resp.getWriter(); writer.print("Hello World!"); writer.flush();
这样,一个直接向客户端(请求者)返回“Hello World!”字符串的Servlet就这样写完了,当下文Tomcat部署好之后,浏览器直接访问相应的页面,就能看到一个Hello World出现在你的浏览器屏幕。
就这?
是的。因为这确实就这么简单,但是当咱们业务复杂起来之后,那可就有得你写了。不过,那是后话了,咱们现在学的是原理,诚然框架非常好使,刷刷刷就写完了,但是总归要知道为什么这样,否则只是只知其然,而不知其所以然。
欸欸欸?不是说好要跑起来吗?
别急,咱还没搞好Tomcat呢
梦的第二步,Tomcat
前面说到过,要让服务程序真正跑起来,还得Tomcat来干那些IO的脏活累活。
首先,得去Tomcat的官网下载好Tomcat.
注意,Tomcat 10版本和9版本在Servlet的包路径上有很大的不同,原因同上,还是Oracle那家伙搞的麻烦...
如果你用的是javax.servlet
请使用Tomcat 9版本;如果使用的是jakarta.servlet
,请使用Tomcat 10。
下载完后,你能得到apache-tomcat-10.1.8-windows-x64.zip
这样的压缩包,把它解压到你喜欢的地方。
-
配置 接下来,又是喜闻乐见的环境配置环节了🥰
在系统变量中,添加
CATALINA_HOME
和CATALINA_BASE
两个环境变量,指向你刚刚解压的Tomcat路径,如我这里解压到了D:\apache-tomcat-10.1.8
,环境变量的值就填D:\apache-tomcat-10.1.8
配置好之后,直接运行bin里边的
startup.bat
在启动tomcat。如果控制台中文乱码了,可以修改conf下的
logging.properties
文件,将里边的utf-8
替换为936
,保存之后再启动,就好了如果一切成功,控制台的输出应该是像这样的:
咱来conf文件夹瞧瞧。
可以看到,有
server.xml
,web.xml
和content.xml
这三个配置文件。麻了,怎么又是XML...
咱一个一个来看
server.xml
负责配置服务器相关的配置参数,如监听端口,最大连接数等等最底层的IO配置。context.xml
配置的是servlet内容的路径等,tomcat服务器会定时去扫描这个文件。一旦发现文件被修改,就会自动重新加载这个文件,而不需要重启服务器。web.xml
这家伙可就大了。不管虽然说是大,但其实不是很大,主要都是注解之类的。web.xml
基本上可以不用动,只需要在咱们的Servlet类中用上@WebServlet
注解,Tomcat就能好心帮我们挂载上去了。当然,如果你想自己手动写
web.xml
部署,也可以自己去了解了解,相信我,了解完之后你肯定会回来用注解的。打开你的浏览器,访问
http://127.0.0.1:8080
,如果你在server.xml
里设置的端口不是8080
,请以你的端口设置为准,如果一切正常,可以看到Tomcat默认的管理页面: -
做点小修改 为了让Web部署更方便,咱们用注解来指定部署路径,用『Maven』来打包成war包进行部署。
图中加了个
@WebServlet("/yahaha")
,表明这个Servlet的路径是/yahaha
,当访问到这个路径的时候,Tomcat就会去就调用我们写的这个Servlet。接着,为了让我们亲爱的的Maven把项目打包成war文件,向项目的
pom.xml
的添加以下内容:<packaging>war</packaging> <build> <finalName>learn</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> </plugins> </build>
好了之后,用maven打包,就得到了咱们想要的war文件了。
-
放置War包 把刚刚得到的war包拿出来(learn.war)。不难注意到,我们在
pom.xml
中,用<finalName>learn</finalName>
指定了最后打包输出的文件名为learn.war
,只要你喜欢,起什么名字都好。把它放到tomcat的
webapps
文件夹里。如果没有启动Tomcat,就把它启动起来。如果你已经把tomcat开起来了,放到webapps
文件夹下面之后,就能看到这么一行日志:14-May-2023 09:13:32.437 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployWAR 正在部署web应用程序存档文件[D:\apache-tomcat-10.1.8\webapps\learn.war] 14-May-2023 09:13:32.465 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployWAR web应用程序存档文件[D:\apache-tomcat-10.1.8\webapps\learn.war]的部署已在[27]ms内完成
同时也能看到
webapps
文件夹下面多出了个learn
,Tomcat帮我们解压好了war包。 -
让我看看! 打开你的浏览器,访问
http://127.0.0.1:8080/learn/yahaha
,就能看到咱们写的Servlet输出了那令人激动的Hello World!
了!注意,我们刚刚写的Servlet路径地址并不是
http://127.0.0.1:8080/yahaha
,而是http://127.0.0.1:8080/应用名/servlet路径
,这里的应用名,就是咱们war包的名字。如果你想从根路径开始,可以把原来webapps
里的ROOT
文件夹删掉,也就是前面咱们看到的那个管理页面,然后将刚刚得到的learn.war
该名成ROOT.war
,然后像刚刚那样放到webapps
文件夹下,咱们的这个war就会被部署挂载到根路径/
下了,此时咱们写的Servlet路径就是/yahaha
了。好了,这个就是你写的第一个Servlet了。能坚持走到这里,或许非常的不容易,看到这个
Hello World!
,或许可能会心中一股激动,这可是你写的第一个Web程序,这是从你的程序里,经过重重关卡发送到浏览器展示出来的文字,不亚于当年前辈们在地球上听到《东方红》时候的激动,指不定还会热泪盈眶,感慨万分;这么一行文字,可能倾注了你一天,或者好几天的时间和经历,但是这都没有关系,最终咱们还是做出来了,这是你自己第一次写出来的真正的Web程序,你这个时候,你已经正式踏上了后端开发的满满长路了。好了,先不发电了。
意犹未尽,继续玩
就输出一个Hello World!怎么够?咱要玩大的!
HttpServlet下有很多方法可以用,获取到的都是些Http参数,这些就是Tomcat帮我们解析好的,直接拿来就用。
例如,通过getParameter(String name)
就能拿到请求参数,如/yahaha?id=1234&type=none
中的id
和type
,可以通过req.getParameter("id")
和req.getParameter("type")
拿到相应的值,如果没有这个参数,获取到的就是null
。这个看文档一看就知道。
下面,咱们就来改写一下前面的Hello World,让他能够根据提供的参数,来输出不同的内容。
像这样,通过String id = req.getParameter("id");
拿到参数中的id,然后再在下面输出出来。
重新打包部署好,访问之前的地址:http://127.0.0.1:8080/learn/yahaha
啊,输出了Hello null!
。这是因为咱们还没给参数呢,当然是null。
没给,那就给嘛。http://127.0.0.1:8080/learn/yahaha?id=lensfrex
这次咱给了id参数进去,可以看到,hello的内容已经不是null了,而是咱们提供的参数了。你喜欢的话,可以换成其他的参数,访问之后就能看到响应了不同的内容。
就这样,咱们的服务不是死的了,能根据请求的不同发生变化了。
到了这里,最基本的Servlet和Tomcat,大家应该是已经知道了。剩下的,就是去了解HttpServletRequest
和HttpServletResponse
的API用法了。这些文档就有很多的解释,也可以到处去看看教程,有很多实例可以学习学习。
After Story
这里带大家了解了http, Servlet以及Tomcat,看起来好像很麻烦的样子。是的,就是很麻烦,但是上古程序员就是这么走来的。
现在咱们有了『SpringBoot』以及『Spring』一系列的框架,这玩意可以说是Java能如此流行的最重要的原因。当然,没有Spring,也会有Summer,也会有Winter。SpringBoot Web已经给我们内置了一个Tomcat,而且帮我们自动配置好了一大堆东西,这些在下一部分中陈姐会带你们领略领略。当你学完Spring的时候,就能知道这真的是个非常牛的玩意。
SpringBoot Web诚然用的非常爽,但是还是不要忘了本,不论上层怎么玩,还是要知道这玩意最基本的东西是怎么一回事的,Servlet还是要了解知道一些的,万变不离其宗,Spring再牛,最基础的还是这些。
当你玩转SpringBoot的web之后,再来回头看看的时候,哈,原来是这么一回事。
另外,Servlet容器不只是有Tomcat,他的同事还有Jetty
, GlassFish
, Undertow
等等,只不过Tomcat因为SpringBoot Web默认内置,因此用的也是最多的,如果你喜欢,或者有需求,也可以换成其他的Servlet容器。
DLC - 课外读物
servlet的本质是什么,它是如何工作的? - bravo1988的回答