哔哩哔哩视频
一、什么是Servlet
servlet的基本概念
1、Servlet是JavaEE的规范之一,规范就是接口
2、Servlet是JavaWeb三大组件之一,三大组件分别是:Servlet程序、Filter过滤器、Listener监听器
3、Servlet是运行在服务器上的一个Java小程序,它可以接收客户端发送的请求,并响应数据给客户端
二、实现Servlet程序
1、编写一个类实现Servlet接口
2、实现Service方法,处理请求,并相应数据
3、到web.xml中去配置servlet程序的访问地址
1、第一个servlet程序
(1)创建HelloServlet类并实现接口Servlet
// 实现Servlet接口,并实现其方法
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet被访问了!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
(2)web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- servlet标签给Tomcat配置Servlet程序 -->
<servlet>
<!-- servlet-name标签 : Servlet程序的别名,一般是servlet类的类名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class标签 : servlet程序的全类名 -->
<servlet-class>pdsu.edu.wbb.HelloServlet</servlet-class>
</servlet>
<!-- servlet-mapping标签 : 给servlet程序配置访问地址 -->
<servlet-mapping>
<!-- servlet-name标签 : 告诉服务器当前配置的地址是给那个Servlet程序用的,和servlet标签中的servlet-name的值相同 -->
<servlet-name>HelloServlet</servlet-name>
<!-- url-pattern标签 : 配置访问地址,在定义 -->
<url-pattern>/hello</url-pattern>
<!--
这里的‘/hello’中,'/'在服务器解析时表示地址为:http://ip:port/工程路径
/hello 表示地址为:http://ip:port/工程路径/hello
-->
</servlet-mapping>
</web-app>
我们启动并运行项目后,会自动访问index.jsp页面,我们只需要在地址栏中填写 hello 即可访问我们所创建的servlet程序,并在控制台输出相应的提示信息。
此时为index.jsp页面,我们在地址栏输入hello并访问,可以看到控制台输出的信息
2、url地址如何定位到servlet程序并访问的?
例如:http://localhost:8080/javaweb_servlet_war_exploded/hello 是我们要访问的一个地址
http:// 表示http协议
:localhost 服务器ip
:8080 端口号
/javaweb_servlet_war_exploded 工程路径
/hello 资源路径
其中,ip地址是唯一的,8080端口号确定了tomcat服务器,通过工程路径确定访问哪个工程,最后再根据资源路径,访问到web.xml文件,通过web.xml文件找到对应的Servlet类并执行程序
3、Servlet的生命周期
1、执行Servlet构造器方法
2、执行init初始化方法
3、执行service方法
4、执行destroy销毁方法
我们在HelloServlet类中分别在各方法中写入一句话并访问Servlet程序,看控制台输出的内容判断Servlet的生命周期
public class HelloServlet implements Servlet {
public HelloServlet() {
System.out.println("1、构造器方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2、init初始化方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3、service方法");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4、destroy销毁方法");
}
}
4、Servlet的请求分发处理
(1)通过继承HttpServlet实现Servlet程序
1、编写一个类去继承HttpServlet类
2、根据业务需要重写doGet或doPost方法
3、到web.xml文件中配置Servlet程序的访问地址
(2)代码实现Servlet的请求分发处理
1、创建一个类继承HttpServlet,并实现doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost方法");
}
}
2、配置web.xml文件
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>pdsu.edu.wbb.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
3、写一个jsp页面用于发送post/get请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="hello2" method="post">
<input type="submit" value="访问">
</form>
</body>
</html>
执行程序
当index.jsp中的form表单的method为post时,点击访问按钮,控制台输出 doPost方法
当index.jsp中的form表单的method为get时,点击访问按钮,控制台输出 doGet方法
三、Servlet类的继承体系
(1)ServletConfig类
ServletConfig类的三大作用
1、获取Servlet程序的别名servlet-name的值
2、获取初始化参数init-param
3、获取ServletContext对象
1、在init()方法中使用servletConfig参数调用
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("HelloServlet程序的别名 : " + servletConfig.getServletName());
System.out.println("初始化参数username的值 : " + servletConfig.getInitParameter("username"));
System.out.println("初始化参数password的值 : " + servletConfig.getInitParameter("password"));
System.out.println("ServletContext对象 : " + servletConfig.getServletContext());
}
2、在web.xml文件中配置init-param
3、访问Servlet程序,控制台输出
注意:当我们重写init方法时,需要调用父类的super.init(config)操作
(2)ServletContext类
1、什么是ServletContext?
1、ServletContext 是一个接口,它表示 Servlet 上下文对象
2、一个 web 工程,只有一个ServletContext 对象实例。
3、ServletContext 对象是一个域对象。
4、ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
2、什么是域对象?
域对象,是可以像 Map 一样存取数据的对象,叫域对象。这里的域指的是存取数据的操作范围,整个 web 工程。
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
3、ServletContext类的四个作用
1、获取 web.xml 中配置的上下文参数 context-param
2、获取当前的工程路径,格式: /工程路径
3、获取工程部署后在服务器硬盘上的绝对路径
4、像 Map 一样存取数据
(1)创建ContextServlet类并继承HttpServlet类
public class ContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取 web.xml 中配置的上下文参数 context-param
ServletContext servletContext = getServletConfig().getServletContext();
String username = servletContext.getInitParameter("username");
System.out.println(username);
// 2、获取当前的工程路径,格式: /工程路径
System.out.println("当前工程路径: " + servletContext.getContextPath());
// 3、获取工程部署后在服务器硬盘上的绝对路径
// 这里的 / 被服务器解析为:http://ip:port/工程名/
System.out.println("工程部署的绝对路径 : " + servletContext.getRealPath("/"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
(2)配置web.xml文件
<servlet>
<servlet-name>ContextServlet</servlet-name>
<servlet-class>pdsu.edu.wbb.ContextServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextServlet</servlet-name>
<url-pattern>/context</url-pattern>
</servlet-mapping>
<!-- context-param是上下文参数,它属于整个web工程,可配置多组 -->
<context-param>
<param-name>username</param-name>
<param-value>zhangsan</param-value>
</context-param>
(3)访问Servlet程序
(4)向ServletContext作用域中存放数据并取出数据
public class ContextServlet2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
servletContext.setAttribute("key1","value");
System.out.println("从 servletContext作用域中获取数据key1的值 : " + servletContext.getAttribute("key1"));
}
}
web.xml文件
<servlet>
<servlet-name>ContextServlet2</servlet-name>
<servlet-class>pdsu.edu.wbb.ContextServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextServlet2</servlet-name>
<url-pattern>/context2</url-pattern>
</servlet-mapping>
四、HttpServletRequest类
1、HttpServletRequest类的作用
每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中,然后传递到service方法(doGet和doPost)进行使用。我们可以通过HttpServletRequest对象获取到所有请求的信息。
2、HttpServletRequest常用方法
方法 | 作用 |
---|---|
getRequestURI() | 获取请求的资源路径 |
getRequestURL() | 获取请求的统一资源定位符(绝对路径) |
getRemoteHost() | 获取客户端的 ip 地址 |
getHeader() | 获取请求头 |
getParameter() | 获取请求的参数 |
getParameterValues() | 获取请求的参数(多个值的时候使用) |
getMethod() | 获取请求的方式 GET 或 POST |
setAttribute(key, value) | 设置域数据 |
getAttribute(key) | 获取域数据 |
getRequestDispatcher() | 获取请求转发对象 |
(1)创建RequestServlet类
获取请求的资源路径
获取请求的统一资源定位符(绝对路径)
获取客户端的ip地址
获取请求头
获取请求方发GET或POST
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的资源路径
System.out.println("URI : " + req.getRequestURI());
// 获取请求的统一资源定位符(绝对路径)
System.out.println("URL : " + req.getRequestURL());
// 获取客户端的ip地址
System.out.println("IP地址 : " + req.getRemoteHost());
// 获取请求头
System.out.println("请求头User-Agent : " + req.getHeader("User-Agent"));
// 获取请求方发GET或POST
System.out.println("请求方法 : " + req.getMethod());
}
}
(2)配置web.xml文件
<servlet>
<servlet-name>RequestServlet</servlet-name>
<servlet-class>request.RequestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet</servlet-name>
<url-pattern>/requestServlet</url-pattern>
</servlet-mapping>
(3)访问servlet程序,查看控制台的执行结果
到浏览器中查看请求头中的User-Agent信息
(5)创建ParamServlet类
获取请求的参数
获取请求的参数(多个值的时候使用)
public class ParamServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 获取请求参数(多个值)
String[] hobbies = req.getParameterValues("hobby");
System.out.println("姓名: " + username);
System.out.println("密码: " + password);
System.out.println("兴趣爱好: " + Arrays.asList(hobbies));
}
}
(6)配置web.xml文件
<servlet>
<servlet-name>ParamServlet</servlet-name>
<servlet-class>request.ParamServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ParamServlet</servlet-name>
<url-pattern>/paramServlet</url-pattern>
</servlet-mapping>
(7)index.jspu页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="paramServlet" method="get">
姓名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">java
<input type="checkbox" name="hobby" value="C">C
<input type="submit" value="访问">
</form>
</body>
</html>
代码执行的结果,获取到了index.jsp页面发送的内容
(3)解决Post请求的乱码问题
1、使用post请求获取页面的请求信息会出现中文乱码
当我们使用post请求让doPost方法接收请求参数时,发现中文会出现乱码,get请求和doGet方法却不会
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 获取请求参数(多个值)
String[] hobbies = req.getParameterValues("hobby");
System.out.println("姓名: " + username);
System.out.println("密码: " + password);
System.out.println("兴趣爱好: " + Arrays.asList(hobbies));
}
2、解决post请求乱码
我们只需要在doPost方法的第一行加上req.setCharacterEncoding("utf-8")
,用于设置请求信息的字符集编码即可
// 设置请求信息的字符集编码
req.setCharacterEncoding("utf-8");
(4)请求转发
1、什么是请求转发
请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发
2、用代码创建例子进行说明
1、从客户端传入一个request参数,先到Servlet1并在其中向request作用域中存入数据
2、使用请求转发到Servlet2中,检查传到的数据是否有在Servlet1中存放数据
(1)Servlet1类
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在 Servlet1(柜台 1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到 Servlet2(柜台 2)去查看
req.setAttribute("key1", "柜台 1 的章");
// 问路:Servlet2(柜台 2)怎么走
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到 IDEA 代码的 web 目录
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// 走向 Servlet2(柜台 2)
requestDispatcher.forward(req, resp);
}
}
(2)Servlet2类
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在 Servlet2(柜台 2)中查看参数(材料):" + username);
// 查看 柜台 1 是否有盖章
Object key1 = req.getAttribute("key1");
System.out.println("柜台 1 是否有章:" + key1);
// 处理自己的业务
System.out.println("Servlet2 处理自己的业务 ");
}
}
(3)web.xml配置文件
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>request.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Servlet2</servlet-name>
<servlet-class>request.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
我们在浏览器的地址栏中写入 servlet1?username=zhangsan 可以看到编译器的控制台上输出了servlet1和servlet2的内容,说明servlet1和servlet2都被访问了, 地址栏中的地址却始终是servlet1,且request作用域中的数据可以在Servlet2被访问,可以得出客户端向服务器出只发送了一次请求
3、请求转发的特点
(1)浏览器地址栏没有发生变化可以得出请求转发发送一次请求
(2)他们可以共享到request作用域中的数据
(3)请求转发可以访问到WEB-INF目录下的资源,但不能访问到工程以外的资源
五、HttpServletResponse 类
(1)HttpServletResponse类的作用
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,
我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置
(2)HttpServletResponse中的两个输出流的说明
字符流 | getWriter() | 常用于回传字符串 |
---|---|---|
字节流 | getOutputStream() | 常用于下载(传递二进制数据) |
注意:两个流不能一起使用,否则会报错
(3)创建ResponseServlet类
1、在类中创建了两个输出流
public class ResponseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();
}
}
2、配置web.xml文件
<servlet>
<servlet-name>ResponseServlet</servlet-name>
<servlet-class>response.ResponseServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet</servlet-name>
<url-pattern>/responseServlet</url-pattern>
</servlet-mapping>
3、测试结果
浏览器访问servlet,报错信息 getOutputStream() has already been called for this response
意为 已为此响应调用getOutputStream()
当将两行代码交换位置时 getWriter() has already been called for this response
意为 已为此响应调用getWriter()
PrintWriter writer = resp.getWriter();
ServletOutputStream outputStream = resp.getOutputStream();
(4)向客户端回传数据
利用输出流向客户端回传数据,getWriter()
public class ResponseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
// writer.println("responseServlet ......");
writer.write("responseServlet ......");
}
}
但传输中文数据时,客户端数据出现乱码
writer.write("客户端回传的数据");
(5)解决响应的乱码问题
我们现获取到服务器的字符集编码,发现是ISO-8859-1,这个字符集编码是不支持中文的,所以我们需要手动设置字符集编码。
当我们设置了服务器的字符集编码为utf-8后,发现浏览器上的数据还是乱码,这是因为,客户端可服务器端的编码方式不一样
浏览器当前的字符集编码格式,当然我们可以通过修改浏览器的字符集编码的方式来解决乱码(但是这显然很不明智,这步是需要用户去操作的,所以我们应该通过程序去修改客户端的字符集编码)
设置客户端的字符集编码
// 通过响应头,设置浏览器的字符集编码
resp.setHeader("Content-Type","text/html;charset=utf-8");
更简单的方式,同时设置客户端和服务器的字符集编码
// 同时设置客户端、服务器和响应头的字符集编码
resp.setContentType("text/html;charset=utf-8");
注意:该设置字符集编码的方法需要在获取流对象之前设置,否则不会生效
(6)请求重定向
1、请求重定向的概念
请求重定向是指客户端给服务器发送请求,但是服务器由于某些原因不能被访问,但它能给客户端提供一个新的地址去访问。
2、代码举例演示
(1)Response1类
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("response1被访问");
req.setAttribute("key","value");
// 请求重定向
resp.sendRedirect("response2");
}
}
(2)Response2类
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getAttribute("key"));
System.out.println("response2被访问");
}
}
(3)web.xml配置文件
<servlet>
<servlet-name>Response1</servlet-name>
<servlet-class>response.Response1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Response1</servlet-name>
<url-pattern>/response1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Response2</servlet-name>
<servlet-class>response.Response2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Response2</servlet-name>
<url-pattern>/response2</url-pattern>
</servlet-mapping>
在客户端浏览器地址栏中访问response1后,地址栏中地址自动跳转到response2,并且在控制台输出了Response1中的信息。从浏览器可以看到两次的请求信息,第一次的请求状态码是302,表示重定向,第二次200表示请求成功,所以在Response1中保存到request作用域中的数据在Request2中访问不到。
3、请求重定向的特点
(1)浏览器地址栏会发生变化,是因为客户端向服务器发送了两次请求
(2)因为客户端向浏览器发送了两次请求,所以request作用域中的数据不共享
(3)不能访问WEB-INF下的资源,但是可以访问工程外的资源