- Servlet是Server与Applet的缩写,意思是服务端小程序,使用Java语言编写的服务端程序,Servlet主要运行在服务端,并由服务器调用执行,是一种按照Servlet标准来开发的类,是SUN公司提供的一门用于开发动态Web资源的技术,言外之意就是要实现web开发,需要实现Servlet标准;
- Servlet本质上也是Java类,但要遵循Servlet规范进行编写,没有main方法,它的创建,使用,销毁都由Servlet容器进行管理,言外之意就是写自己的类,不用写main方法,别人自动调用;
- Servlet与HTTP协议是紧密联系的,其可以处理HTTP协议相关的所有内容,这也是Servlet广泛应用的原因;
- 提供了Servlet功能的服务器,叫做Servlet容器,其常见的容器有很多,例如Tomcat,Jetty,WebLogic Server,WebSphere,JBoss等等;
Servlet的实现
- 第一步:创建Web项目,打开IDEA,File->New->Project,如下所示:
- 选择Web Application(4.0),点击下一步;
- 填写项目名称以及工作空间,点击Finish,进入工程主界面,如下所示:
-
第二步:实现Servlet规范,其目的是让自定义的类能够接受网络请求,接受到网络请求之后对请求进行分析,以及业务逻辑处理,具体步骤如下:
- 创建一个Java类 命名为Servlet01,继承自
HttpServlet
; - 重写service方法,进行业务逻辑处理;
- 设置注解,即在完成业务代码编写之后,还需要向服务器说明,特定请求对应特定资源;
- 创建一个Java类 命名为Servlet01,继承自
-
开发servlet项目,在Servlet3.0中,可以使用
@WebServlet注解
将一个继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet; -
案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 实现Servlet
* 1.创建普通Java类;
* 2.实现Servlet规范,继承自HttpServlet;
* 3.重写service方法,用来处理请求;
* 4.设置注解;
*/
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//这行代码要注释,否则浏览器报405错误
//super.service(req, resp);
System.out.println("Hello Servlet!!!");
resp.getWriter().write("Hello Servlet!!!");
}
}
image.png
- 运行之后,会自动打开浏览器,在URL后面添加上面定义的注解
ser01
,浏览器中显示的内容,以及IDEA控制台也会有打印,如下所示:
- 上述的注解有多种不同的写法,以下写法都是等价的,如下:
//@WebServlet("/ser01")
//@WebServlet(name = "Servlet01",value = "ser01")
//@WebServlet(name = "Servlet01",value = {"ser01","ser001"})
@WebServlet(name = "Servlet01",urlPatterns = {"ser01","ser001"})
Servlet--服务器的设置
- 刚在浏览器中输入的URL为:
http://localhost:8080/Servlet01_war_exploded/ser01
,其中localhost:8080
是本机地址与8080端口,Servlet01_war_exploded
是项目的对外访问地址,ser01
是注解,其中项目的对外访问地址是可以设置的,如下所示:
Snip20211208_51.png
- 再次运行项目,手动打开浏览器,然后输入
http://localhost:8080/Servlet01/ser01
即可;
Servlet的工作流程
- 当我们在浏览器中输入
http://localhost:8080/Servlet01/ser01
时,其本质是浏览器客户端向Tomcat服务器发送了一次Servlet请求,其工作原理如下:- 首先根据
localhost
定位到当前计算机; - 然后根据
8080
端口,定位到Tomcat服务器程序,因为在安装Tomcat时指定其默认端口为8080; - 接着根据
Servlet01
定位到指定项目; - 其次根据注解
ser01
,定位到该项目中的指定资源,然后执行如下: - 1)Web服务器(Tomcat)会先检查是否已经装载并创建了该Servlet的实例对象,如果是,则直接执行第4步,否则执行第2步;
- 2)装载并创建了该Servlet的一个实例对象;
- 3)调用Servlet实例对象的init()方法,进行初始化;
- 4)创建一个用于封装HTTP请求消息的
HTTPServletRequest对象
和一个代表HTTP响应消息的HTTPServletResponse对象
,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去; - 5)WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法;
- 首先根据
Servlet的其他实现方式
- 上面实现了一种创建Java类继承自HttpServlet的实现方式,还有其他的实现方式:
- 创建Java类继承继承自
GenericServlet
; - 创建Java类实现Servlet接口;
- 创建Java类继承继承自
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/ser02")
public class Servlet02 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("继承自GenericServlet的Servlet");
}
}
package com.sf.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/ser03")
public class Servlet03 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("实现Servlet接口的Servlet");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
Servlet的生命周期
- 首先Servlet是没有main方法的,不能独立运行,它的运行完全由Servlet引擎来控制和调度,其生命周期如下所示:
-
实例化和初始化
:当前请求达到Web容器(Tomcat服务器)时,容器会查找该servlet实例对象是否存在,如果不存在,则会创建servlet实例对象并初始化,调用init()方法
; -
就绪/调用/服务阶段
:容器调用servlet实例对象的service方法
,处理请求的方法在整个生命周期中可以被多次调用; -
销毁
:当容器关闭时(应用程序停止时),会将程序中的servlet实例对象进行销毁,调用destoty()方法
; - 案例代码:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser05")
public class Servlet05 extends HttpServlet {
@Override
public void destroy() {
System.out.println("Servlet被销毁");
}
@Override
public void init() throws ServletException {
System.out.println("Servlet被初始化");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet正在处理请求");
}
}
- 控制台调试结果如下:
- 总结:Servlet的生命周期分为四步,Servlet类的加载->实例化->服务->销毁;
- Tomcat与Servlet的工作时序图如下所示:
- WebClient向Servlet容器(Tomcat)发出HTTP请求;
- Servlet容器接收WebClient的请求;
- Servlet容器创建一个HttpServletRequest对象,将WebClient请求信息封装到这个对象中;
- Servlet容器创建一个HttpServletResponse对象,
- Servlet容器调用HttpServlet对象的service方法,将Request与Response作为参数,传递给HttpServlet;
- HttpServlet调用HttpServletRequest对象的有关方法,获取Http请求信息;
- HttpServlet调用HttpServletResponse对象的有关方法,生成响应数据;
- Servlet容器把HttpServlet的响应结果返回给WebClient;
HttpServletRequest对象
- HttpServletRequest对象:主要是用来接收客户端发送过来的请求信息,包括请求的参数,请求头等等,service()方法中形参接收的是HttpServletRequest接口的实例化对象,表示该对象主要应用在HTTP协议上,该对象是由Tomcat封装好,传递过来的;
- 在HttpServletRequest接口中,定义的方法很多,但基本都是围绕接收客户端参数的,常用方法有如下:
-
getRequestURL
:获取请求的完整URL,从http开始,到?结束; -
getRequestURI
:获取请求的部分路径,从项目站点名开始,到?结束; -
getQueryString
:获取请求的参数字符串; -
getMethod
:获取请求方式; -
getProtocol
:获取请求的协议版本; -
getContextPath
:获取项目的站点名(项目的对外访问路径); -
getParameter
:获取指定名称的参数; -
getParameterValues
:获取指定名称的参数的所有参数值;
-
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 实现Servlet
* 1.创建Java类
* 2.实现Servlet规范,继承自HttpServlet
* 3.重写service方法,用来处理请求
* 4.设置注解,指定访问的路径
*/
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
resp.getWriter().write("Hello Servlet");
System.out.println("请求的完整URL:" + req.getRequestURL().toString());
System.out.println("请求的部分路径:" + req.getRequestURI());
System.out.println("请求的参数字符串" + req.getQueryString());
System.out.println("请求方式" + req.getMethod());
System.out.println("请求的协议版本" + req.getProtocol());
System.out.println("项目的站点名" + req.getContextPath());
//获取指定名称的参数
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println("name:" + name + " password:" + password);
//获取指定名称的参数的所有参数值
String[] s = req.getParameterValues("s");
if (s != null && s.length > 0) {
for (String str : s) {
System.out.println("s:" + str);
}
}
}
}
- 浏览器输入:
http://localhost:8080/servlet01/ser01?name=admin&psd=asd123&s=sing&s=dance&s=speak
,控制台打印结果如下:
请求乱码问题
-
Tomcat8.0及以上版本,GET请求在解析参数时不会出现乱码,但POST请求在解析参数时会出现乱码;
-
Tomcat8.0以下版本,GET请求在解析参数时会出现乱码,POST请求在解析参数时也会出现乱码;
-
Request请求属于接收客户端的参数,其有默认的语言编码为ISO-8859-1,此编码不支持中文,所以解析时肯定会出现乱码,解决方案有如下:
- 在Request中设置编码方式,告诉服务器以指定的编码方式来解析数据,即
req.setCharacterEncoding("UTF-8")
,注意此设置只针对POST请求才有效; - 解决Tomcat8以下 get请求 参数解析乱码的方案:
String sf_name = new String(req.getParameter("name").getBytes("ISO-8859-1"),"UTF-8")
- 在Request中设置编码方式,告诉服务器以指定的编码方式来解析数据,即
-
下面模拟一下POST请求,解析参数出现乱码的情况:创建JSP文件
login.jsp
,内容如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<form method="post" action="ser01">
姓名: <input type="text" name="name"> <br>
密码: <input type="password" name="password"> <br>
<button>登录</button>
</form>
</body>
</html>
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 实现Servlet
* 1.创建Java类
* 2.实现Servlet规范,继承自HttpServlet
* 3.重写service方法,用来处理请求
* 4.设置注解,指定访问的路径
*/
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
//获取指定名称的参数
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println("name:" + name + " password:" + password);
}
}
- 在浏览器中输入
http://localhost:8080/servlet01/login.jsp
,出现登录界面,账号密码均输入张三
,然后IDEA控制台解析POST请求参数时出现乱码,如下所示:
- 现在将
Servlet01
中代码作如下修改:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
//request设置编码方式
req.setCharacterEncoding("UTF-8");
//获取指定名称的参数
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println("name:" + name + " password:" + password);
}
}
- 然后再次在浏览器中输入,访问登录页,输入账号密码,Servlet容器在参数解析不会出现乱码了,如下所示:
- 解决Tomcat8以下 get请求 参数解析乱码(很少用了),代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
//request设置编码方式
req.setCharacterEncoding("UTF-8");
//获取指定名称的参数
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println("name:" + name + " password:" + password);
//解决Tomcat8以下 get请求 参数解析乱码
String sf_name = new String(req.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
System.out.println("name:" + sf_name);
}
}
请求转发
- 请求转发:是一种服务器的行为,当客户端请求达到后,服务器进行转发,此时会
将请求对象进行保存,地址栏中的URL地址不会改变
,得到响应之后,服务器端再将响应发送给客户端
,从始至终只有一个请求发出
,实现了请求数据的共享
,可实现页面的跳转
; - 案例代码:
- Servlet04文件
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser04")
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收参数
String name = req.getParameter("name");
System.out.println("Servlet04 name:" + name);
}
}
- login.jsp文件
<%--
Created by IntelliJ IDEA.
User: liyanyan33
Date: 2021/12/8
Time: 下午4:46
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
$END$
</body>
</html>
- login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> 登录 </h1>
</body>
</html>
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser03")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收参数
String name = req.getParameter("name");
System.out.println("Servlet03 name:" + name);
//请求转发到Servlet04
req.getRequestDispatcher("ser04").forward(req,resp);
//请求转发给jsp文件
req.getRequestDispatcher("login.jsp").forward(req,resp);
//请求转发给html文件
req.getRequestDispatcher("login.html").forward(req,resp);
}
}
Request作用域
- Request作用域可以看成是一个对象,通过该对象可以在一个请求中传递数据,作用范围是:在一次请求中有效,即服务器跳转有效;
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/ser05")
public class Servlet05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet05");
//设置域对象的内容
req.setAttribute("name","admin");
req.setAttribute("age",20);
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
req.setAttribute("list",list);
//设置请求转发Servlet06
req.getRequestDispatcher("ser06").forward(req,resp);
//设置请求转发jsp
}
}
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("/ser06")
public class Servlet06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet06");
//获取域对象
String name = (String) req.getAttribute("name");
System.out.println("name: " + name);
Integer age = (Integer) req.getAttribute("age");
System.out.println("age: " + age);
List<String> list = (List<String>) req.getAttribute("list");
if (list != null && list.size() > 0) {
System.out.println(list.get(0));
}
}
}
- 运行项目,在浏览器中输入
http://localhost:8080/servlet01/ser05
,结果如下:
HttpServletResponse对象
- HttpServletResponse对象:是服务器端对客户端的请求进行响应,将Web服务器处理后的结果返回给客户端;
- Servlet容器接收到客户端请求之后,可以通过HttpServletResponse对象直接进行响应,响应时需要获取输出流,输出流有两种形式:
- 字符输出流:getWriter()
- 字节输出流:getOutputStream()
- 两种输出流不能同时使用,否则会报错;
- 案例代码:
package com.sf.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ser07")
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取字符输出流
PrintWriter writer = resp.getWriter();
writer.write("Hello");
//获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("World".getBytes());
//不能同时使用 会报错
}
}
响应乱码问题
- 在响应中,若响应的内容中含有中文,有可能出现乱码问题,这时因为服务器响应的数据会经过网络传输,服务器端有一种编码方式,在客户端也有一种编码方式,当两端的编码方式不同时,就会出现乱码;
- 针对字符输出流getWriter(),响应中文必定会出现乱码,因为服务端在进行编码时默认使用ISO-8859-1格式的编码,该编码只支持中文;
- 解决方案:
- 分别设置服务端响应与客户端响应的编码格式;
- 同时设置服务端响应与客户端响应的编码格式;
- 案例代码:
package com.sf.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ser07")
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.分别设置服务端与客户端的编码格式
//设置服务端的编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端的编码格式
resp.setHeader("content-type","text/html;charset=UTF-8");
//2.同时设置服务端与客户端的编码格式
resp.setContentType("text/html;charset=UTF-8");
//获取字符输出流
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
}
}
- 针对字节输出流getOutputStream(),解决方案与上面的完全相同,代码如下:
package com.sf.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ser07")
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.分别设置服务端与客户端的编码格式
//设置服务端的编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端的编码格式
resp.setHeader("content-type","text/html;charset=UTF-8");
//2.同时设置服务端与客户端的编码格式
resp.setContentType("text/html;charset=UTF-8");
//获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("<h2>你好</h2>".getBytes());
}
}
HttpServletResponse对象 -- 重定向
- 重定向:是一种服务器指导,客户端的行为;
- 当客户端发出第一个请求,被服务端接收处理之后,服务端会进行响应,在响应的同时,服务端会给客户端一个新的地址(下次请求的地址),当客户端接收到响应后,会立刻根据服务端给的新地址发起第二个请求,服务端接收请求并作出响应,重定向完成;
- 在重定向中会存在两个请求,属于客户端行为;
- 客户端浏览器的地址栏会发生改变;
- Request对象不共享;
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser08")
public class Servlet08 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet08");
//获取请求参数
String name = req.getParameter("name");
System.out.println("name: " + name);
//请求重定向到Servlet09
resp.sendRedirect("ser09");
}
}
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser09")
public class Servlet09 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet09");
//获取请求参数
String name = req.getParameter("name");
System.out.println("name: " + name);
}
}
- 在浏览器中输入
http://localhost:8080/servlet01/ser08?name=yanzi
,按下回车键,地址栏会变成http://localhost:8080/servlet01/ser09
,且控制台打印如下:
请求转发与重定向的区别
- 不同点:
- 请求转发是一次请求,数据在Request域中共享,重定向是两次请求,数据在Request域中不共享;
- 请求转发是服务端行为,重定向是客户端行为;
- 请求转发时地址栏不发生变化,重定向时地址栏会发生变化;
- 请求转发时的地址只能是当前站点(当前项目)下的资源,重定向时的地址可以是任意的地址,能实现跨域;
- 相同点:
- 两者都能实现跳转;
Cookie对象
-
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于Cookie是服务端保存在客户端的信息,所以其安全性也是很差的,例如常见的记住密码则可以通过Cookie来实现;
-
有一个专门操作Cookie的类
javax.servlet.http.Cookie
,跟随服务端的响应发送给客户端,保存在浏览器,当下次再访问服务器是把Cookie再带回服务器; -
Cookie的格式:键值对,多个键值对用分号隔开;
-
Cookie的创建与发送
:通过new Coookie()
方法来创建Cookie对象,然后添加到response对象中,接着随着响应发送给客户端; -
案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser02")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
//创建Cookie
Cookie cookie = new Cookie("name","admin");
//添加到response 发送给客户端
resp.addCookie(cookie);
}
}
- 运行项目,在Google浏览器中输入
http://localhost:8080/Servlet01/ser02
,然后浏览器中,右键->检查,如下所示:
-
Cookie的获取
:在服务端只提供了一个getCookies()的方法来获取客户端回传的所有cookie组成的一个数组,如果需要获取单个cookie则需要通过遍历,getName()获取Cookie的名称,getValue()获取Cookie的值; - 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ser02")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet!!!");
//获取cookie数组
Cookie[] cookies = req.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie: cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.printf("cookie名称:" + name + ",值:" + value);
}
}
}
}
-
Cookie设置到期时间
:Cookie存在有效时间的,默认为当前浏览器关闭就会失效,我们可以手动设置cookie的有效时间,通过setMaxAge(int time)
方法来设置cookie的最大有效时间,以秒为单位; - 到期时间的取值情况如下 :
- 负整数:表示不存储该cookie,其maxAge属性的默认值为就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失;
- 正整数:表示存储的秒数,浏览器会把cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间;
- 等于0,表示删除该cookie,浏览器内存中和客户端硬盘上都会删除该cookie对象;
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/cookie03")
public class Cookie03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie1 = new Cookie("name1","zhangsan");
cookie1.setMaxAge(-1);
resp.addCookie(cookie1);
Cookie cookie2 = new Cookie("name2","lisi");
cookie2.setMaxAge(60);
resp.addCookie(cookie2);
Cookie cookie3 = new Cookie("name3","wangwu");
cookie3.setMaxAge(0);
resp.addCookie(cookie3);
}
}
-
Cookie的注意点
- cookie保存在当前浏览器中,不能跨浏览器保存;
- cookie存中文有问题,正常情况下cookie是不能出现中文的,如果有中文则通过
URLEncoder.encode()
来进行编码,获取时通过URLDecoder.decode()
来进行解码; - 同名cookie问题,如果服务端发送重复的cookie那么会覆盖原来的cookie;
- 浏览器存放cookie的数量是有上限的,cookie时存储在客户端的,一般由服务端创建和设定,后期结合Session来实现回话跟踪;
- cookie存储的数据是有大小限制的,一般上限为4KB左右;
- 案例代码:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@WebServlet("/cookie04")
public class Cookie04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = "姓名";
String vaule = "李四";
//编码
name = URLEncoder.encode(name);
vaule = URLEncoder.encode(vaule);
Cookie cookie = new Cookie(name,vaule);
resp.addCookie(cookie);
Cookie[] cookies = req.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cook: cookies) {
//解码
System.out.println(URLDecoder.decode(cook.getName()));
System.out.println(URLDecoder.decode(cook.getValue()));
}
}
}
}
-
Cookie的路径
:通过setPath()方法设置cookie的路径,这个路径直接决定了服务器的请求是否会从浏览器中加载某些cookie
; - 情景一:当前服务器下任何项目的任意资源都可以获取cookie对象;
- 情景二:当前项目下的资源可获取cookie对象,默认不设置cookie的路径;
- 情景三:指定项目下的资源可获取cookie对象;
- 情景四:指定目录下的资源可获取cookie对象;
- 总结:只有访问的路径中包含Cookie对象的Path,才可以获取到Cookie对象;
- 案例代码:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@WebServlet("/cookie05")
public class Cookie05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//当前服务器下任何项目的任意资源都可以获取cookie对象
Cookie cookie1 = new Cookie("cookie01","cooki01");
cookie1.setPath("/");
resp.addCookie(cookie1);
//当前项目下的资源可获取cookie对象,默认不设置cookie的路径
//http://localhost:8080/servlet01/
Cookie cookie2 = new Cookie("cookie02","cooki02");
cookie2.setPath("/servlet01");
resp.addCookie(cookie2);
//指定项目下的资源可获取cookie对象
Cookie cookie3 = new Cookie("cookie03","cooki03");
cookie3.setPath("/servlet02");
resp.addCookie(cookie3);
//当前项目 指定目录下的资源可获取cookie对象
Cookie cookie4 = new Cookie("cookie04","cooki04");
cookie4.setPath("/servlet01/ser01");
resp.addCookie(cookie4);
}
}
HttpSession对象
- HttpSession对象:是javax.servlet.http.HttpSession的实例,属于HTTP协议的范畴;
- 对于服务器而言,每一个脸接到它的客户端都是一个session,servlet容器使用此接口创建HTTP客户端和HTTP服务器之间的会话,会话将保留指定的时间段,跨多个连接或来自用户的页面请求,一个会话通常对应一个用户,该用户可能多次访问一个站点,可以通过此接口查看和操作某个会话的信息,比如会话标识符,创建时间和最后一次访问时间,在整个Session中,最重要的就是属性的操作;
- session无论客户端还是服务端都可以感知到,若重新打开一个新的浏览器,则无法获取之前的session,因为每一个session只保存在当前浏览器中,并在相关的页面取得;
- session的作用是为了标识一次会话,或者说是确认一个用户,在一次会话(一个用户的多次请求)期间共享数据,我们可通过
request.getSession()
方法,来获取当前会话的session对象; - 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/session01")
public class Session01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取会话对象
HttpSession session = req.getSession();
//获取会话id
System.out.println(session.getId());
//获取会话创建的时间
System.out.println(session.getCreationTime());
//获取会话最后一次访问时间
System.out.println(session.getLastAccessedTime());
//判断是否是新的会话
System.out.println(session.isNew());
}
}
- 获取session对象时,若session对象存在时,则直接获取,若session对象不存在时,则创建session对象;
标识符JSEESIONID
- session是为了标识一次会话,其标识就是会话id即sessionId;
- 每当一次请求到达服务器时:
- 如果开启了会话,服务器第一步会查看是否从客户端回传一个名为JSEESIONID的cookie,
- 如果没有则认为这是一次新的会话,会创建一个新的session对象,并用唯一的sessionid为此会话作一个标志;
- 如果有JSEESIONID这个cookie回传,服务器则会根据JSEESIONID这个值去查看是否含有id为JSEESIONID值的session对象,如果没有则认为是一个新的会话,则重新创建一个新的session对象,并用唯一的sessionid为此会话作一个标志,如果找到了对应的session对象,则认为是之前标志过的一次会话,返回该session对象,数据实现共享;
- JSEESIONID是一个cookie,这是一个比较特殊的cookie,当请求到达服务器时,如果访问了session,则服务器会创建一个名为JSEESIONID,值为获取到的session(无论是获取到的还是新创建的)的sessionid的cookie对象,并添加到response对象中,响应给客户端,有效时间为关闭浏览器;
- Session的底层是依赖Cookie来实现的;
session域对象
- Session表示一次会话,在一次会话过程中数据是可以共享的,这时session作为域对象存在,可以通过
setAttribute(name,value)
方法往域对象中添加数据,通过getAttribute(name)
从域对象中获取数据,通过removeAttribute(name)
从域对象中移除数据; - 数据存储在session域对象中,当session对象不存在了,或者两个不同的session,数据也就不能共享了;
- 请求转发时的案例如下:
<%--
Created by IntelliJ IDEA.
User: liyanyan33
Date: 2021/12/8
Time: 下午4:46
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
//获取session域对象
String s_name = (String) request.getSession().getAttribute("s_name");
String s_password = (String) request.getSession().getAttribute("s_password");
out.print("s_name: " + s_name + ",s_password: " + s_password);
//获取request域对象
String r_name = (String) request.getAttribute("r_name");
out.print(",r_name: " + r_name);
%>
</body>
</html>
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/session02")
public class Session02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取会话对象
HttpSession session = req.getSession();
//设置session域对象
session.setAttribute("s_name","lisi");
session.setAttribute("s_password","asd123");
//移除session域对象
session.removeAttribute("s_password");
//设置request域对象
req.setAttribute("r_name","zhangsan");
//请求转发
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}
- 浏览器输入
http://localhost:8080/servlet01/session02
,调试结果如下:
image.png - 请求重定向时,代码修改如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/session02")
public class Session02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取会话对象
HttpSession session = req.getSession();
//设置session域对象
session.setAttribute("s_name","lisi");
session.setAttribute("s_password","asd123");
//移除session域对象
session.removeAttribute("s_password");
//设置request域对象
req.setAttribute("r_name","zhangsan");
//请求重定向
resp.sendRedirect("index.jsp");
}
}
- 浏览器输入
http://localhost:8080/servlet01/session02
,调试结果如下:
- 总结:
- 请求转发时,request作用域有效,session作用域有效;
- 请求重定向时,request作用域失效,session作用域有效;
Session对象的销毁
-
默认到期时间
:当客户端第一次请求servlet且操作session时,session对象生成,Tomcat中session默认存活时间为30分钟,一旦操作,session就会重新计时; - 默认到期时间可以在Tomcat/conf/web.xml文件中进行修改的,如下所示:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
-
手动设置到期时间
:通过设置session的最大不活动时间,调用setMaxInactiveInterval()方法,单位为秒; -
立即失效
:调用invalidate()方法,让session立即失效; -
关闭浏览器就会失效
:session底层依赖cookie,cookie在浏览器关闭时就会失效; -
关闭服务器就会失效
;
ServletContext对象
- 每一个Web应用都有且仅有一个ServletContext对象,又称为Application对象,其与应用程序相关,在Web容器启动时,会为每一个Web应用程序创建一个对应的ServletContext对象;
- ServletContext对象有两大作用:
- 作用域对象实现共享数据,此数据在整个应用程序中实现共享;
- 该对象中保存了当前应用程序的相关信息,可通过相关Api获取;
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/scontext01")
public class ServletContext01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取ServletContext对象
//1.通过request对象获取
ServletContext context1 = req.getServletContext();
//2.通过session对象获取
ServletContext context2 = req.getSession().getServletContext();
//3.通过ServletConfig获取
ServletContext context3 = getServletConfig().getServletContext();
//4.直接获取
ServletContext context4 = getServletContext();
//常用方法
//获取当前服务器的版本信息
String serverInfo = context1.getServerInfo();
System.out.println("当前服务器的版本信息: " + serverInfo);
//获取项目的真实路径
String realPath = context1.getRealPath("/");
System.out.println("项目的真实路径:" + realPath);
}
}
- 控制台打印结果如下:
ServletContext域对象
- ServletContext可以作为域对象,能存数据,使得整个应用程序共享数据,但不建议存放过多的数据,因为ServletContext中的数据一旦存储进去没有手动移除将会一直存在;
- Servlet中有三大域对象:
- request域对象,请求转发时有效,重定向时失效;
- session域对象,请求转发和重定向时均有效,session销毁时失效;
- ServletContext域对象,在整个应用程序中有效,服务器关闭时失效;
//1.通过request对象获取
ServletContext context1 = req.getServletContext();
context1.setAttribute("name","yanzi");
context1.setAttribute("password","asd123");
context1.removeAttribute("password");
String name = (String) context1.getAttribute("name");
文件的上传
- 文件上传涉及到前台页面的编写和后台服务端代码的编写,前台发送文件,后台接收并保存文件,实现文件的完整上传;
- 首先
文件上传的前台页面
编写如下: - 创建一个
upload.html
文件,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="uploadServlet">
姓名: <input type="text" name="name"> <br>
文件: <input type="file" name="myfile"> <br>
<button>提交</button>
</form>
</body>
</html>
-
首先是定义表单;
-
设置表单的提交类型为post;
-
设置表单类型为文件上传表单 enctype="multipart/form-data";
-
设置文件提交的地址;
-
准备表单元素:
- 普通表单项 type="text";
- 文件表单项 type="file"
-
设置表单项的name属性值,否则后台无法接收数据;
-
然后
文件上传的后台代码
编写:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
@WebServlet("/uploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传");
//设置请求的编码
req.setCharacterEncoding("UTF-8");
//获取普通表单项
String name = req.getParameter("name");
System.out.println("name: " + name);
//获取上传文件
Part part = req.getPart("myfile");
//通过part对象 获取上传的文件名
String fileName = part.getSubmittedFileName();
System.out.println("上传文件名: " + fileName);
//文件存放路径
String filePath = req.getServletContext().getRealPath("/");
System.out.println("文件存放路径: " + filePath);
//上传文件到执行目录
part.write(filePath + "/" + fileName);
}
}
- 运行项目,在浏览器中输入
http://localhost:8080/Servlet07/upload.html
,然后选择文件text.txt
,点击提交,进入后台,IDEA控制台打印如下:
image.png - 查看文件存放路径,确实上传成功;
文件下载
- 将服务器上的资源文件下载到本地,可通过
超链接进行下载
,或者通过后台代码下载
; - 超链接下载:使用a标签;
- 当超链接遇到浏览器能识别的资源时(例如txt,png,jpg文件能识别),默认不会下载,可通过download属性进行下载;
- 当超链接遇到浏览器不能识别的资源时,会自动下载;
- 首先准备资源文件并设置路径,如下所示:
- 然后创建download.html文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件下载</title>
</head>
<body>
<a href="download/test.txt">文本文件</a>
<a href="download/tk.png">图片文件</a>
<a href="download/xxx.zip">压缩文件</a>
<hr>
<a href="download/test.txt" download=>文本文件</a>
<a href="download/tk.png" download="推客.png">图片文件</a>
</body>
</html>
- 浏览器输入
http://localhost:8080/Servlet07/download.html
,看到如下:
-
后台代码下载的实现
,创建DownloadServlet
文件;- 需要设置响应类型,浏览器无法使用某种方式或激活某个程序来处理的MIME类型,即
resp.setContentType("application/x-msdownload")
; - 需要设置响应头,即
resp.setHeader("Content-Disposition","attachment;filename=" + fileName)
; - 读取下载的文件,调用
resp.getOutputStream()
向客户端写入内容;
- 需要设置响应类型,浏览器无法使用某种方式或激活某个程序来处理的MIME类型,即
- 案例代码如下:
package com.sf.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件下载");
//设置请求编码格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//获取文件名
String fileName = req.getParameter("fileName");
if (fileName == null || "".equals(fileName.trim())) {
resp.getWriter().write("请输入要下载的文件名");
resp.getWriter().close();
return;
}
System.out.println("文件名: " + fileName);
//获取图片的存放路径
String path = req.getServletContext().getRealPath("/download/");
System.out.println("目标文件路径: " + path);
//通过路径获取file对象
File file = new File(path + fileName);
if (file.exists() && file.isFile()) {
resp.setContentType("application/x-msdownload");
resp.setHeader("Content-Disposition","attachment;filename=" + fileName);
//输入流
InputStream in = new FileInputStream(file);
//字节输出流
ServletOutputStream out = resp.getOutputStream();
//byte数组
byte[] bytes = new byte[1024];
int length = 0;
while ((length = in.read(bytes)) != -1) {
//输出
out.write(bytes,0,length);
}
//关闭资源
out.close();
in.close();
} else {
resp.getWriter().write("文件不存在,请重试");
resp.getWriter().close();
}
}
}
-
download.html
文件代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件下载</title>
</head>
<body>
<a href="download/test.txt">文本文件</a>
<a href="download/tk.png">图片文件</a>
<a href="download/xxx.zip">压缩文件</a>
<hr>
<a href="download/test.txt" download=>文本文件</a>
<a href="download/tk.png" download="推客.png">图片文件</a>
<form action="downloadServlet">
文件名: <input type="text" name="fileName" placeholder="请输入要下载的文件名">
<button>下载</button>
</form>
</body>
</html>
- 运行项目,浏览器中输入
http://localhost:8080/Servlet07/download.html
,然后输入文件名test.txt
,进行文件下载;
网友评论