Servlet
The hurricane seeks the shortest road by the no-road, and suddenly ends its search in the Nowhere.
风于无路之中寻求最短之路,又突然地在“无何有之国”终之了它的追求。
Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
Servlet原理
Servlet运行过程
Servlet的工作过程
步骤:
- Web Client 向Servlet容器(Tomcat)发出Http请求
- Servlet容器接收Web Client的请求
- Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中。
- Servlet容器创建一个HttpResponse对象
- Servlet容器调用HttpServlet对象的doservice方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet 对象。
- HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。
- HttpServlet调用HttpResponse对象的有关方法,生成响应数据。
- Servlet容器把HttpServlet的响应结果传给Web Client。
- 客户端的网络请求首先会被Http服务器接收(也叫Web服务器、web容器,其需要提供web应用运行所需的环境,接收客户端的Http请求);
- Web服务器根据请求的路径将请求转交给对应的Servlet容器(也称Servlet引擎,为Servlet的运行提供环境支持,可以理解为tomcat或其他服务器);
- Servlet容器根据对应的虚拟路径(@WebServlet中配置的)来加载Servlet,如果Serlvet没有被实例化则创建该Servlet的一个实例(调用init方法);
- Servlet容器根据用户的HTTP请求,创建一个ServletRequest对象(HTTP的请求信息被封装在其中)和一个可以对HTTP请求进行响应的ServletResponse对象(类似于寄信,并在信中说明回信的地址),然后调用HttpServlet中重写的service(ServletRequest req, ServletResponse res)方法,并在这个方法中,将ServletRequest、ServletResponse这两个对象向下转型,得到我们非常熟悉的HttpServletRequest和HttpServletResponse两个对象,然后将客户端的请求转发到HttpServlet中protected修饰的service(HttpServletRequest req, HttpServletResponse resp);
- service(HttpServletRequest req, HttpServletResponse resp)根据请求的method(get、post、put、delete、head、options、trace)来调用不同的方法,如doGet、doPost;
- 服务端处理完Http的请求后,根据HttpServletResponse对象将处理结果作为Http响应返回给客户端。
Servlet UML关系图
ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;
共享数据
1
2ServletContext context = this.getServletContext();
context.setAttribute("数据key",数据value); //将一个数据保存获取初始化参数
1
2
3
4
5<!--web.xml-->
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/javaweb</param-value> </context-param>1
2
3
4
5protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}请求转发
1
2
3
4
5
6@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
context.getRequestDispatcher("/forwardpath").forward(req,resp);
}读取资源文件
Properties
在java目录下新建properties
在resources目录下新建properties
1
2username=root12312
password=zxczxczxc
发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:
1
2
3
4
5
6
7
8
9@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB- INF/classes/com/bobo/xx.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user+":"+pwd);
}
HttpServletResponse
简单分类
负责向浏览器发送数据的方法
1
2ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;从中我们可以看到,getOutputStream()方法返回ServletOutputStream对象,更适合向客户端写入二进制数据,并且Servlet容器不会对这些二进制数据进行编码,因此我们常用ServletOutputStream来向客户端发送如图片、文件等内容;对于getWriter()方法返回的PrintWriter对象,里面封装了更多的写入字符文本的函数,并且我们上文提到的setContentType()方法设置的MIME类型对其输出内容有效,因此也可以很好地解决中文乱码问题。
还有一点需要注意的是,这两个方法在一个response对象中不可以同时调用,否则会抛出一个IllegalStateException,也就是非法状态异常,因为输出流只能有一个(如果可以多次获取的话,客户端又如何确认哪个Http响应是最后一个呢)。
负责向浏览器发送响应头的方法
1
2
3
4
5
6
7
8
9
10void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);相应状态码的常量
| Name | discribtion | 释义 |
| :–: | :———————-: | :—————————————: |
| 200 | SC_OK | 此次请求已经成功 |
| 301 | SC_MOVED_PERMANENTLY | 请求的网页已永久移动到新位置 |
| 302 | SC_MOVED_TEMPORARILY | 临时移动、请求地址不变 |
| 401 | SC_UNAUTHORIZED | 未授权、用户需登录 |
| 403 | SC_FORBIDDEN | 服务器拒绝了此次请求(权限问题) |
| 404 | SC_NOT_FOUND | 服务器没找到URI匹配的 |
| 405 | SC_METHOD_NOT_ALLOWED | 调用的方法不允许使用(get、post不匹配) |
| 500 | SC_INTERNAL_SERVER_ERROR | 服务器内部发生异常,请求中断 |
| 502 | SC_BAD_GATEWAY | 网关错误(如Nginx),无法收到服务器的响应 |
| 504 | SC_GATEWAY_TIMEOUT | 请求超时,在约定时间内没有收到Http响应 |常见应用
向浏览器输出信息
下载文件
- 要获取下载文件的路径
- 下载的文件名
- 使浏览器可以支持下载 (Content-disposition) 中文文件名用 URLEncoder.encode 编码,否则可能乱码
- 获取下载文件输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46package com.bobo.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
//1.要获取下载文件的路径
String realPath = "绝对路径";
System.out.println("下载的文件的路径:" + realPath);
//2.下载的文件名
//substring()截取字符串 lastIndexOf()截取/之后的字符串
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3.使浏览器可以支持下载 (Content-disposition) attachment:以附件方式下载 中文文件名用 URLEncoder.encode 编码,否则可能乱码
resp.setHeader("Content-disposition","attachment;filename" + URLEncoder.encode("fileName","utf-8"));
//4.获取下载文件输入流
FileInputStream in = new FileInputStream(realPath);
//5.创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//6.获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//7.将FileOutputStream流入到buffer缓冲区
//in.read(buffer)
//8.使用OutputStream将缓冲区中的数据输出到客户端
while ((len = in.read(buffer))> 0){
out.write(buffer,0,len);
}
in.close();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}验证码功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60package com.bobo.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5s自动刷新一次
resp.setHeader("refresh","3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D g = (Graphics2D) image.getGraphics();
//设置图片背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告诉浏览器这个请求用图片方式打开
resp.setContentType("image/png");
//网站存在缓存 不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("pragma","no-cache");
//将图片写给浏览器
boolean write = ImageIO.write(image,"png", resp.getOutputStream());
}
//生成随机数
private String makeNum(){
Random random = new Random();
//6个9 代表6位数
String num = random.nextInt(999999) + "";
StringBuffer sb = new StringBuffer();
//保证输出6位数
for (int i = 0; i < 6-num.length() ; i++) {
sb.append("0");
}
String s = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}实现重定向
1
2
3
4//resp.setHeader("location","/");
//resp.setStatus(302);//重定向常量
//重定向一定要注意路径问题
resp.sendRedirect("/img"); //重定向
HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过HttpServletRequest方法,获得客户端的所有信息。
方法 | 描述 |
---|---|
Cookie[] getCookies() | 返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。 |
ServletInputStream getInputStream() | 使用 ServletInputStream,以二进制数据形式检索请求的主体。 |
String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称。 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分。 |
String getHeader(String name) | 以字符串形式返回指定的请求头的值。 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
String getPathInfo() | 当请求发出时,返回与客户端发送的 URL 相关的任何额外的路径信息。 |
String getProtocol() | 返回请求协议的名称和版本。 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串。 |
String getRemoteAddr() | 返回发送请求的客户端的互联网协议(IP)地址。 |
String getRemoteHost() | 返回发送请求的客户端的完全限定名称。 |
String getRemoteUser() | 如果用户已通过身份验证,则返回发出请求的登录用户,或者如果用户未通过身份验证,则返回 null。 |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。 |
String getRequestedSessionId() | 返回由客户端指定的 session 会话 ID。 |
String getServletPath() | 返回调用 JSP 的请求的 URL 的一部分。 |
int getContentLength() | 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。 |
int getIntHeader(String name) | 返回指定的请求头的值为一个 int 值。 |
int getServerPort() | 返回接收到这个请求的端口号。 |
int getParameterMap() | 将参数封装成 Map 类型。 |
获取前端参数,请求转发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.bobo.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//后台接收中文乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("============================");
System.out.println(username);
System.out.println(password);
System.out.println("============================");
//通过请求转发
// 这里的/代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
}
Cookie_Session
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
Cookie
Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Session
除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
点击跳转Cookie_Session详解- 本文作者:bobo
- 本文链接:https://boyolo.github.io/article/55715.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!