重温Servlet,为Spring Framework和Spring BOOT之基础
1、一个Servlet入门项目
使用STS4新建一个空的POM项目:
(1)创建包com.edison
(2)创建类ServletTest,继承自HttpServlet,代码如下:
(3)使用mvn compile编译
(4)到本项目的target目录下
拷贝classes目录到tomcat的webapps\ROOT\WEB-INF目录下:
(5)打开这个目录下的web.xml,并增加如下几行(阴影部分):
(6)到tomcat的bin目录下,执行startup.bat
(7)使用浏览器测试:
2、Servlet架构和依赖环境
Java Servlet 是运行在 HTTP Server或应用服务器(例如:Tomcat)上的程序,它是作为来自 Web browser和 HTTP server应用程序之间的中间层。
Servlet收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页,这些功能和 CGI(Common Gateway Interface,公共网关接口)程序相同。
相比于 CGI,Servlet更容易开发,还有如下特点:
l Servlet 在 Web 服务器的地址空间内执行。
l Servlet 用 Java 编写。
l Java 安全管理器能保护Servlet。
l Java 类库能支撑 Servlet。
JAVA Web开发框架大部分都是建立在Servlet基础之上的,比如Structs和Spring MVC,前者的ActionServlet和后者的DispatcherServlet都是Servlet。
Servlet是处理web请求的基本单元,而Filter则是围绕着Servlet,用于在web请求被处理之前或者之后对web请求(Request)和应答(Response)修改,Filter工作机制如下图:
Servlet Filter 具有如下特点:
l Filter既可以作用于Servlet之前、又可以作用于Servlet之后。而Spring Security作用于Servlet之前
l Filter在Request到达Servlet之前,可以直接将Response返回,此功能用于诸如在未登录的情况下直接向用户展示登录页面这样的功能。
l 多个Filter起作用时有先后顺序。
Servlet依赖环境包括Web服务器(应用服务器、容器)、servlet规范、JSP规范。
Java Servlet 运行在带有支持 Java Servlet 规范的Web服务器或者容器上,而Tomcat就是一种支持servlet容器功能的应用服务器。
Servlet需要 javax.servlet 和 javax.servlet.http 包支持。
Servlet必须符合Java Servlet 规范和 JSP 规范,不同容器实现的规范版本不同。
3、Servlet主要功能和生命周期
Servlet用于替代CGI,因此具有CGI类似的功能:
l 获取浏览器上传的用户数据,例如网页上的 HTML 表单(也可以是来自 applet 或自定义的 HTTP 客户端程序的表单)。
l 获取浏览器上传的HTTP请求数据,包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
l 处理这些上行数据并生成结果,包括访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务。
l 下发用户响应数据到浏览器,例如文本文件(HTML 、XML、JSON)、图片、流媒体等。
l 下发 HTTP响应数据到浏览器,包括返回文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
Servlet 生命周期是从创建直到毁灭的整个过程,过程包括:
l init () 方法:初始化,只调用一次。
l service() 方法:处理客户端的请求,每收到一次请求,服务器新产生一个线程来处理。
l destroy() 方法: 终止,只调用一次。
l 垃圾回收:JVM 的垃圾回收器负责最后的垃圾回收。
生命周期示意图如下:
service() 方法可以调用的HTTP请求方法:
l doGet() 方法
对应HTTP GET 请求,来自于一个 URL 的正常请求,或者来自于一个未指定 METHOD 的 HTML 表单:
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码}
l doPost() 方法
对应HTTP POST 请求,来自于一个特别指定了 METHOD 为 POST 的 HTML 表单:
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码}
4、HTTP和HTTPS协议介绍
HTTP(HyperText Transfer Protocol超文本传输协议),是WWW(World Wide Web,万维网)的传输协议,是TCP/IP模型的应用层协议,负责Web浏览器和Web服务器之间传输超文本信息(Hypertext),超文本信息可以描述超媒体( Hypermedia),而超媒体包括文本、图形、视频、音频等多媒体(Multimedia)信息,这些信息以web页面(Web Page)为单位,他们之间通过超链接(Hyperlink)相互连接。
HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
HTTP1.0采用普通交互流程,多次交互才能完成信息传输,如下:
(1)Web浏览器连接到Web服务器
Web浏览器与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。
(2)发送HTTP请求
通过TCP套接字,Web浏览器向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据等组成。
(3)Web服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。Web服务器将资源复本写到TCP套接字,由Web浏览器读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
(4)释放连接TCP连接
若连接模式为close,则Web服务器主动关闭TCP连接,Web浏览器释放TCP连接;若连接模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
(5)Web浏览器解析HTML内容
Web浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。Web浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在Web浏览器窗口中显示。
HTTP1.1采用持久连接(HTTP Persistent Connections),也称为 HTTP keep-alive 或 HTTP connection reuse,只要任意一端没有明确提出断开连接,则保持TCP连接状态,其交互流程如下:
上图的"客户端"实际上就是Web浏览器,或者定制的特殊Web浏览器,上图的"服务器"就是Web服务器,"建立TCP连接"包括三个子步骤,"断开TCP连接"包括四个子步骤。
持久连接使得多数请求以管线化(pipelining)方式发送成为可能,HTTP1.1支持管线化传输。管线化技术支持并行发送多个请求。如下图:
HTTPS在HTTP基础上增加了数字签名和非对称加解密功能,其交互图如下:
HTTP报文包括请求报文和响应报文两大部分,其中请求报文由请求行(request line)、请求头(header)、空行和请求体四个部分组成。而响应报文由状态行、响应头部、空行和响应体四个部分组成。下图是一个具体HTTP报文例子:
HTTP请求类型包括:
HTTP状态码包括:
代码 | 消息 | 描述 |
100 | Continue | 只有请求的一部分已经被服务器接收,但只要它没有被拒绝,客户端应继续该请求。 |
101 | Switching Protocols | 服务器切换协议。 |
200 | OK | 请求成功。 |
201 | Created | 该请求是完整的,并创建一个新的资源。 |
202 | Accepted | 该请求被接受处理,但是该处理是不完整的。 |
203 | Non-authoritative Information | |
204 | No Content | |
205 | Reset Content | |
206 | Partial Content | |
300 | Multiple Choices | 链接列表。用户可以选择一个链接,进入到该位置。最多五个地址。 |
301 | Moved Permanently | 所请求的页面已经转移到一个新的 URL。 |
302 | Found | 所请求的页面已经临时转移到一个新的 URL。 |
303 | See Other | 所请求的页面可以在另一个不同的 URL 下被找到。 |
304 | Not Modified | |
305 | Use Proxy | |
306 | Unused | 在以前的版本中使用该代码。现在已不再使用它,但代码仍被保留。 |
307 | Temporary Redirect | 所请求的页面已经临时转移到一个新的 URL。 |
400 | Bad Request | 服务器不理解请求。 |
401 | Unauthorized | 所请求的页面需要用户名和密码。 |
402 | Payment Required | 您还不能使用该代码。 |
403 | Forbidden | 禁止访问所请求的页面。 |
404 | Not Found | 服务器无法找到所请求的页面。. |
405 | Method Not Allowed | 在请求中指定的方法是不允许的。 |
406 | Not Acceptable | 服务器只生成一个不被客户端接受的响应。 |
407 | Proxy Authentication Required | 在请求送达之前,您必须使用代理服务器的验证。 |
408 | Request Timeout | 请求需要的时间比服务器能够等待的时间长,超时。 |
409 | Conflict | 请求因为冲突无法完成。 |
410 | Gone | 所请求的页面不再可用。 |
411 | Length Required | "Content-Length" 未定义。服务器无法处理客户端发送的不带 Content-Length 的请求信息。 |
412 | Precondition Failed | 请求中给出的先决条件被服务器评估为 false。 |
413 | Request Entity Too Large | 服务器不接受该请求,因为请求实体过大。 |
414 | Request-url Too Long | 服务器不接受该请求,因为 URL 太长。当您转换一个 "post" 请求为一个带有长的查询信息的 "get" 请求时发生。 |
415 | Unsupported Media Type | 服务器不接受该请求,因为媒体类型不被支持。 |
417 | Expectation Failed | |
500 | Internal Server Error | 未完成的请求。服务器遇到了一个意外的情况。 |
501 | Not Implemented | 未完成的请求。服务器不支持所需的功能。 |
502 | Bad Gateway | 未完成的请求。服务器从上游服务器收到无效响应。 |
503 | Service Unavailable | 未完成的请求。服务器暂时超载或死机。 |
504 | Gateway Timeout | 网关超时。 |
505 | HTTP Version Not Supported | 服务器不支持"HTTP协议"版本。 |
在HTTP/1.1种规定了47种首部字段(图表参考《图解HTTP》):
(1)通用首部字段
(2)请求首部字段
(3)响应首部字段
(4)实体首部字段
(5)其他首部字段
Cookie、Set-Cookie、Content-Disposition、Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、Trailer、TE、Transfer-Encoding、Upgrade etc...
5、在Java EE中测试Servlet
这里提供GET和POST表单请求的例子,步骤如下:
(1)新建web项目
使用STS4创建Dynamic Web新项目:
下一步如下:
点"New Runtime":
选择Tomcat7,然后Next,再点击Browse选择tomcat安装目录:
选择目录后回到先前的界面,然后再选择JRE:
然后选择两次Next,如下:
点finish。到左边的Project Explorer:
选中src,右键菜单new-->servlet,进入如下界面:
填写包和类名后,两次next后:
点Finish。生成TestPostMethod.java文件,内容如下:
修改doGet,如下:
修改doPost,如下:
在Project Explorer的web content目录下新建目录:HTML。然后,选中HTML,右键菜单new-->html file,弹出"New HTML File"窗口,在File Name中填写:index.htm,如下:
点Finish。Index.html代码如下:
修改为:
选中WEB-INF目录,右键菜单new-->
点Next,如下:
在File Name中,输入web.xml,点Finish。Web.xml内容为:
修改为:
(2)构建和测试
选中项目,右键菜单Run As-->Run on Server:
点Finish。
这时候,STS4在右上角自动显示首页:
在上面输入:google和www.google.com,点提交:
返回后再下面选中物理,点"选择学科":
过程中遇到一个问题:
[SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:TestPost' did not find a matching property.
解决:在STS4的右下角,选中Server,再选中Tomcat,如下图:
然后,右键菜单Open,在右上角窗口,把"Publish module contexts to separate XML files"打钩,如下:
关闭并保存。
6、Servlet请求
Servlet请求类是javax.servlet.http.HttpServletRequest。
每当Web浏览器请求一个页面时,Web服务器就会产生一个新的request对象来代表这个请求。 request对象提供了一系列方法来获取HTTP请求头,包括表单数据,cookies,HTTP方法等等。
常用的HTTP请求头如下:
头信息 | 描述 |
Accept | 这个头信息指定浏览器或其他客户端可以处理的 MIME 类型。值 image/png 或 image/jpeg 是最常见的两种可能值。 |
Accept-Charset | 这个头信息指定浏览器可以用来显示信息的字符集。例如 ISO-8859-1。 |
Accept-Encoding | 这个头信息指定浏览器知道如何处理的编码类型。值 gzip 或 compress 是最常见的两种可能值。 |
Accept-Language | 这个头信息指定客户端的首选语言,在这种情况下,Servlet 会产生多种语言的结果。例如,en、en-us、ru 等。 |
Authorization | 这个头信息用于客户端在访问受密码保护的网页时识别自己的身份。 |
Connection | 这个头信息指示客户端是否可以处理持久 HTTP 连接。持久连接允许客户端或其他浏览器通过单个请求来检索多个文件。值 Keep-Alive 意味着使用了持续连接。 |
Content-Length | 这个头信息只适用于 POST 请求,并给出 POST 数据的大小(以字节为单位)。 |
Cookie | 这个头信息把之前发送到浏览器的 cookies 返回到服务器。 |
Host | 这个头信息指定原始的 URL 中的主机和端口。 |
If-Modified-Since | 这个头信息表示只有当页面在指定的日期后已更改时,客户端想要的页面。如果没有新的结果可以使用,服务器会发送一个 304 代码,表示 Not Modified 头信息。 |
If-Unmodified-Since | 这个头信息是 If-Modified-Since 的对立面,它指定只有当文档早于指定日期时,操作才会成功。 |
Referer | 这个头信息指示所指向的 Web 页的 URL。例如,如果您在网页 1,点击一个链接到网页 2,当浏览器请求网页 2 时,网页 1 的 URL 就会包含在 Referer 头信息中。 |
User-Agent | 这个头信息识别发出请求的浏览器或其他客户端,并可以向不同类型的浏览器返回不同的内容。 |
Servlet请求类详细情况如下:
序号 | 方法 & 描述 |
1 | Cookie[] getCookies() |
2 | Enumeration getAttributeNames() |
3 | Enumeration getHeaderNames() |
4 | Enumeration getParameterNames() |
5 | HttpSession getSession() |
6 | HttpSession getSession(boolean create) |
7 | Locale getLocale() |
8 | Object getAttribute(String name) |
9 | ServletInputStream getInputStream() |
10 | String getAuthType() |
11 | String getCharacterEncoding() |
12 | String getContentType() |
13 | String getContextPath() |
14 | String getHeader(String name) |
15 | String getMethod() |
16 | String getParameter(String name) |
17 | String getPathInfo() |
18 | String getProtocol() |
19 | String getQueryString() |
20 | String getRemoteAddr() |
21 | String getRemoteHost() |
22 | String getRemoteUser() |
23 | String getRequestURI() |
24 | String getRequestedSessionId() |
25 | String getServletPath() |
26 | String[] getParameterValues(String name) |
27 | boolean isSecure() |
28 | int getContentLength() |
29 | int getIntHeader(String name) |
30 | int getServerPort() |
31 | int getParameterMap() |
修改前面的"servlet入门项目",如下:
测试:
7、Servlet应答
Web服务器处理Servlet请求后向Web浏览器发送Servlet应答消息,设置HTTP状态和向WEB浏览器发送数据,如Cookie、HTTP文件头信息等。
常用的应答头信息如下:
头信息 | 描述 |
Allow | 这个头信息指定服务器支持的请求方法(GET、POST 等)。 |
Cache-Control | 这个头信息指定响应文档在何种情况下可以安全地缓存。可能的值有:public、private 或 no-cache 等。Public 意味着文档是可缓存,Private 意味着文档是单个用户私用文档,且只能存储在私有(非共享)缓存中,no-cache 意味着文档不应被缓存。 |
Connection | 这个头信息指示浏览器是否使用持久 HTTP 连接。值 close 指示浏览器不使用持久 HTTP 连接,值 keep-alive 意味着使用持久连接。 |
Content-Disposition | 这个头信息可以让您请求浏览器要求用户以给定名称的文件把响应保存到磁盘。 |
Content-Encoding | 在传输过程中,这个头信息指定页面的编码方式。 |
Content-Language | 这个头信息表示文档编写所使用的语言。例如,en、en-us、ru 等。 |
Content-Length | 这个头信息指示响应中的字节数。只有当浏览器使用持久(keep-alive)HTTP 连接时才需要这些信息。 |
Content-Type | 这个头信息提供了响应文档的 MIME(Multipurpose Internet Mail Extension)类型。 |
Expires | 这个头信息指定内容过期的时间,在这之后内容不再被缓存。 |
Last-Modified | 这个头信息指示文档的最后修改时间。然后,客户端可以缓存文件,并在以后的请求中通过 If-Modified-Since 请求头信息提供一个日期。 |
Location | 这个头信息应被包含在所有的带有状态码的响应中。在 300s 内,这会通知浏览器文档的地址。浏览器会自动重新连接到这个位置,并获取新的文档。 |
Refresh | 这个头信息指定浏览器应该如何尽快请求更新的页面。您可以指定页面刷新的秒数。 |
Retry-After | 这个头信息可以与 503(Service Unavailable 服务不可用)响应配合使用,这会告诉客户端多久就可以重复它的请求。 |
Set-Cookie | 这个头信息指定一个与页面关联的 cookie。 |
Servlet应答类是javax.servlet.http.HttpServletResponse类,而Response对象是HTTP应答类的一个实例。Web服务器处理Request对象后会创建Response对象。
Response对象定义了处理创建HTTP信息头的接口。通过这个对象处理cookie,时间戳,HTTP状态码等等。
HTTP应答类的详细情况如下:
序号 | 方法 & 描述 |
1 | String encodeRedirectURL(String url) |
2 | String encodeURL(String url) |
3 | boolean containsHeader(String name) |
4 | boolean isCommitted() |
5 | void addCookie(Cookie cookie) |
6 | void addDateHeader(String name, long date) |
7 | void addHeader(String name, String value) |
8 | void addIntHeader(String name, int value) |
9 | void flushBuffer() |
10 | void reset() |
11 | void resetBuffer() |
12 | void sendError(int sc) |
13 | void sendError(int sc, String msg) |
14 | void sendRedirect(String location) |
15 | void setBufferSize(int size) |
16 | void setCharacterEncoding(String charset) |
17 | void setContentLength(int len) |
18 | void setContentType(String type) |
19 | void setDateHeader(String name, long date) |
20 | void setHeader(String name, String value) |
21 | void setIntHeader(String name, int value) |
22 | void setLocale(Locale loc) |
23 | void setStatus(int sc) |
修改前面的"servlet入门项目",如下:
测试: