Servlet
Servlet的执行过程
-
Servlet
作用处理请求 - 当浏览器访问一个
http://localhost:8080/hello/hello
路径, 就向Tomcat
发送一个请求
Servlet的生命周期
-
Servlet
生命周期分为四个状态: 实例化 → 初始化→服务→销毁 - 出生:(实例化-->初始化)第一次访问
Servlet
就出生(默认情况下) - 活着:(服务)应用活着,`Servlet就活着
- 死亡:(销毁)应用卸载了
Servlet
就销毁。
/**
* creater ruirui.ding
*
* @Date: 2019/8/4 11:58
**/
public class HelloServlet implements Servlet {
// 出生: (实例化) 第一次访问Servlet会调用构造方法
public HelloServlet() {
System.out.println("Hello Servlet");
}
// 出生: (实例化) 第一次访问会调用Servlet初始化
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("hello world");
}
// 销毁: 程序停止了才会调用
@Override
public void destroy() {
}
// 服务: 作出相应, 每次访问都会调用Service
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().write("hello Servlet");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
Servlet的创建时机
- 默认情况下
Servlet
在第一次使用Servlet
的时候才调用构造方法和初始化方法, 也就是访问到对应的页面的时候才会调用构造方法和初始化方法 - 可以在
web.xml
中设置load-on-startup
为2,Servlet
就会在tomcat
启动的时候调用构造方法和初始化方法
Servlet实现的三种方式
- 实现
java.servlet.Servlet
-
继承javax.servet.GenericServlet类(适配器模式)
-
继承javax.servlet.http.HttpServlet类(模板方法设计模式)
Servlet的继承结构
Servlet映射细节
规则 | 意义 |
---|---|
url-pattern: *.do |
以*.字符串为结尾的请求都可以访问 注:不要加/ |
url-pattern: /* |
任意字符串都可以访问 |
url-pattern |
/action/* 以/action 开头的请求都可以访问 |
多种匹配规则同时存在, 匹配的优先级为
绝对匹配--> /开头匹配 --> 扩展名方式匹配
ServletContext
Context
:上下文
ServletContext
: 代表的是整个应用。一个应用只有一个ServletContext
对象。是单例对象
作用:
域对象:在一定范围内(当前应用),使多个Servlet
共享数据。
常用方法
API | 注解 |
---|---|
void setAttribute(String name,object value); |
向ServletContext 对象的map 中添加数据 |
Object getAttribute(String name); |
从ServletContext 对象的map 中取数据 |
void rmoveAttribute(String name); |
根据name 去移除数据 |
常用方法示例
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
// 获取一个应用的servlet上下文
ServletContext context = this.getServletContext();
System.out.println("*************:" + context);
// 往上下文存入数据
context.setAttribute("name", ".ding");
// 在上下文取出数据
String name = (String)context.getAttribute("name");
System.out.println("name::" + name);
// 移除name
context.removeAttribute("name");
System.out.println("name2" + (String)context.getAttribute("name"));
}
}
设置/读取ServletContext配置信息
先在web.xml
设置ServletContext
里设置全局配置
<context-param>
<param-name>encoding</param-name>
<param-value>utf8</param-value>
</context-param>
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取一个应用的servlet上下文
ServletContext context = this.getServletContext();
System.out.println("*************:" + context.getInitParameter("encoding"));
}
}
获取资源路径
String getRealPath(String path);
- 根据资源名称得到资源的绝对路径.
- 可以得到当前应用任何位置的任何资源。
相关代码实现
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 在Servlet中读取info.properties数据
String path = this.getServletContext().getRealPath("WEB-INF/" +
"classes/com/rui/servlet/info.properties");
System.out.println(path);
// 创建属性对象
Properties pro = new Properties();
// 加载路径下的资源
pro.load(new FileInputStream(path));
System.out.println(pro.getProperty("username"));
//解决乱码
resp.setHeader("Content-Type", "text/html;charset=utf-8");
resp.getWriter().write(path);
resp.getWriter().write("-----");
resp.getWriter().write(pro.getProperty("username"));
}
}
在IDEA下如何查看本地资源的路径
Servlet的转发
将来自HelloServlet1
的请求转发到HelloServlet2
-
HelloServlet1
的代码
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setHeader("Content-Type", "text/html;charset=utf-8");
RequestDispatcher rd = req.getRequestDispatcher("/HelloServlet2");
rd.forward(req, resp);
}
}
-
HelloServlet2
的代码
@WebServlet("/HelloServlet2")
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("来自HelloServlet2");
}
}
HTTP
简介
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
HTTP请求流程
HTTP的常见请求头
请求头 | 意义 |
---|---|
Accept |
浏览器可接受的MIME 类型。 |
Accept-Charset |
浏览器可接受的字符集。 |
Accept-Encoding |
浏览器能够进行解码的数据编码方式,比如gzip 。Servlet 能够向支持gzip 的浏览器返回经gzip 编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。 |
Accept-Language |
浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 |
Authorization |
授权信息,通常出现在对服务器发送的WWW-Authenticate 头的应答中。 |
Connection |
表示是否需要持久连接。如果Servlet 看到这里的值为Keep-Alive 或者看到请求使用的是HTTP 1.1 (HTTP 1.1 默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet 需要在应答中发送一个Content-Length 头,最简单的实现方法是:先把内容写入ByteArrayOutputStream ,然后在正式写出内容之前计算它的大小。 |
Content-Length |
表示请求消息正文的长度。 |
Cookie |
这是最重要的请求头信息之一 |
From |
请求发送者的email 地址,由一些特殊的Web客户程序 使用,浏览器不会用到它。 |
Host |
初始URL中的主机和端口。 |
If-Modified-Since |
只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified” 应答。 |
Pragma |
指定“no-cache” 值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。 |
Referer |
包含一个URL ,用户从该URL 代表的页面出发访问当前请求的页面。 |
User-Agent |
浏览器类型,如果Servlet 返回的内容与浏览器类型有关则该值非常有用。 |
HTTP常见的响应头
Allow |
服务器支持哪些请求方法(如GET 、POST 等)。 |
Content-Encoding |
文档的编码(Encode )方法。只有在解码之后才可以得到Content-Type 头指定的内容类型。利用gzip 压缩文档能够显著地减少HTML 文档的下载时间。Java 的GZIPOutputStream 可以很方便地进行gzip 压缩,但只有Unix 上的Netscape 和Windows 上的IE 4、IE 5 才支持它。因此,Servlet 应该通过查看Accept-Encoding 头(即request.getHeader("Accept-Encoding") )检查浏览器是否支持gzip ,为支持gzip 的浏览器返回经gzip 压缩的HTML 页面,为其他浏览器返回普通页面。 |
Content-Length |
表示内容长度。只有当浏览器使用持久HTTP 连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream ,完成后查看其大小,然后把该值放入Content-Length 头,最后通过byteArrayStream.writeTo(response.getOutputStream() 发送内容。 |
Content-Type |
表示后面的文档属于什么MIME 类型。Servlet 默认为text/plain ,但通常需要显式地指定为text/html 。由于经常要设置Content-Type ,因此HttpServletResponse 提供了一个专用的方法setContentType 。 |
Date |
当前的GMT 时间。你可以用setDateHeader 来设置这个头以避免转换时间格式的麻烦。 |
Expires |
应该在什么时候认为文档已经过期,从而不再缓存它? |
Last-Modified |
文档的最后改动时间。客户可以通过If-Modified-Since 请求头提供一个日期,该请求将被视为一个条件GET ,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified) 状态。Last-Modified 也可用setDateHeader 方法来设置。 |
Location |
表示客户应当到哪里去提取文档。Location 通常不是直接设置的,而是通过HttpServletRespons e的sendRedirect 方法,该方法同时设置状态代码为302 。 |
Refresh |
表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path") 让浏览器读取指定的页面。 |
Server服务器名字 |
Servlet 一般不设置这个值,而是由Web 服务器自己设置。 |
Set-Cookie |
设置和页面关联的Cookie 。Servlet 不应使用response.setHeader("Set-Cookie", ...) ,而是应使用HttpServletResponse 提供的专用方法addCookie 。 |
WWW-Authenticate |
客户应该在Authorization 头中提供什么类型的授权信息?在包含401(Unauthorized) 状态行的应答中这个头是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"") 。 |
HttpServletResponse
手动实现文件下载
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fileName = "六年级上册数学课件-第四单元《比》(回顾整理)|青岛版(2014秋) " +
"(共9张PPT).ppt";
// 下载文件
String path = "C:/Users/rui/Desktop/" +
fileName;
// 获取文件输入流
FileInputStream fis = new FileInputStream(path);
// 对文件名进行encode编码
fileName = URLEncoder.encode(fileName, "UTF-8");
// 设置文件下载请求头
resp.setHeader("Content-disposition", "attachment;filename="
+ fileName);
// 将文件转成二进制数据
byte [] bs = new byte[1024];
// 获取输出流
ServletOutputStream sos = resp.getOutputStream();
int len = 0;
while ((len = fis.read(bs)) != -1) {
sos.write(bs, 0, len);
}
// 关闭资源
fis.close();
}
}
设置客户端不需要缓存【pass】
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
//浏览器刷新时就不会有缓存
response.addHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
ValidateCode code = new ValidateCode(WIDTH, HEIGHT, 4, 6);
code.write(response.getOutputStream());
}
自动刷新功能
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
//response.setHeader("Refresh", "1");//每隔一秒刷新一次
response.setHeader("Refresh", "5;URL=index.html");//5秒后转到另一页面
response.getWriter().write("注册成功!5秒后会自动跳转到index.html页面");
}
请求重定向
-
请求重定向指:一个
web
资源收到客户端请求后,通知客户端去访问另外一个
web资源`,这称之为请求重定向。 - 重定向特点:地址栏会变,并发送2次请求,增加服务器负担
- 实现方式
response.sendRedirect()
- 实现原理:
302/307
状态码和location
头即可实现重定向
转发特点:地址栏不会变,客户端发送一次请求
重定向实现
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendRedirect("HelloServlet2");
}
}
@WebServlet("/HelloServlet2")
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("HelloServlet2" );
}
}
HttpServletResponse总结
getOutputStream
和getWriter
方法分别用于得到输出二进制数据、输出文本数据getOutputStream
和getWriter
这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 会抛异常。Servlet
程序向ServletOutputStream
或PrintWriter
对象中写入的数据将被Servlet
引擎从response
里面获取,Servlet
引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。Serlvet
的service
方法结束后,Servlet
引擎将检查getWriter
或getOutputStream
方法返回的输出流对象是否已经调用过close
方法,如果没有,Servlet
引擎将调用close
方法关闭该输出流对象。
HttpServletRequest
简介
HttpServletRequest
对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
获得客户端信息
API | 作用 |
---|---|
getRequestURL |
返回客户端发出请求时的完整URL。 |
getRequestURI 方法 |
返回请求行中的资源名部分。 |
getQueryString 方法 |
返回请求行中的参数部分。 |
getRemoteAddr 方法 |
返回发出请求的客户机的IP 地址 |
getRemoteHost 方法 |
返回发出请求的客户机的完整主机名 |
getRemotePort 方法 |
返回客户机所使用的网络端口号 |
getLocalAddr 方法 |
返回WEB 服务器的IP 地址。 |
getLocalName 方法 |
返回WEB 服务器的主机名 |
getMethod |
得到客户机请求方式 |
获得客户端请求头
getHeader(name)
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println(req.getHeader("User-Agent"));
System.out.println(req.getHeader("Accept"));
}
}
getHeaderNames()
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Enumeration<String> e = req.getHeaderNames();
while(e.hasMoreElements()) {
String name = e.nextElement();
System.out.println(name + ":" + req.getHeader(name));
}
}
}
OutPut
host:localhost:8080
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
accept-encoding:gzip, deflate, br
accept-language:zh-CN,zh;q=0.9
cookie:JSESSIONID=7BCC931187E41F2598EBE975DDC50F80
获得客户端请求参数(客户端提交的数据)
API | 作用 |
---|---|
getParameter(name) |
获取一个参数的值 |
getParameterValues(String name) |
获取一个参数对应的多个值, 返回一个数组 |
getParameterNames() |
一次性获取所有参数的名字, 返回一个Enumeration
|
getParameterMap |
返回一个Map<String, String[]> , 前面为参数, 后面为参数对应的值, 存在一个数组中. |
getInputStream |
以字节读取请求参数 |
HttpRequestServlet应用
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String userName = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(userName + ";" + password);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/HelloServlet1">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录"></input>
</form>
</body>
</html>
实现请求转发
- 请求转发指一个
web
资源收到客户端请求后,通知服务器去调用另外一个web
资源进行处理。 -
request
对象提供了一个getRequestDispatcher
方法,该方法返回一个RequestDispatcher
对象,调用这个对象的forward
方法可以实现请求转发。 -
request
对象同时也是一个域对象,开发人员通过request
对象在实现转发时,把数据通过request
对象带给其它web
资源处理。setAttribute方法
getAttribute方法
removeAttribute方法
getAttributeNames方法
@WebServlet("/HelloServlet1")
public class HelloServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/egg.jsp").forward(req,resp);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<div>hahahhahahahah</div>
</body>
</html>
那么访问HelloServlet1
, 就自动转到egg.jsp
上
重定向机制的运作流程
1、用户在浏览器端输入特定URL,请求访问服务器端的某个组件
2、服务器端的组件返回一个状态码为302的响应结果。
3、当浏览器端接收到这种响应结果后,再立即自动请求访问另一个web组件
4、浏览器端接收到来自另一个web组件的响应结果。
HttpServeltRespons
e的sendRedirect(String location)
用于重定向
转发和重定向的区别
- 域对象:
session,request,page
。。。 - 域:区间、范围
- 重定向:以前的
request
中存放的变量全部失效,并进入一个新的request
作用域。 - 转发:以前的
request
中存放的变量不会失效,就像把两个页面拼到了一起。
include(), forward(), sendRedirect()的区别
假定第一次请求的为
servlet1
,处理转发的为servlet2
include()
方法将请求转发给servlet2
,servle2
对该请求做出了的响应并入到原来的servlet1响应对象中,原来的servlet1
还可以继续输出响应信息。forward
方法将请求转发给其他的servlet2
,servlet2
负责对请求做出响应,而原先的servlet1
的执行则终止。sendRedict()
则是在浏览器请求servlet1
之后,重新告诉浏览器将请求重新定位到servlet
, 会进行两次服务器请求。
JSP
1. JSP概述
JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。JSP/Servlet规范。JSP实际上就是Servlet。
JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。
JSP本质就是一个Servlet