一、Servlet技术
1. 了解Servlet
- Servlet 是 Javaee 规范之一,规范就是 api
- Servlet 是 JavaWeb 三大组件之一,三大组件:Servlet程序、Filter过滤器、Listener监听器
- Servlet 是运行在服务器上的 java 程序,用于接收客户端发送的请求,并响应数据给客户端
2. 手动实现Servlet程序
相关步骤:
1)编写一个类实现 Servlet 接口
2)实现接口中的 service 方法,用来处理请求,并响应数据
3)到 web.xml 中,配置 servlet 程序的访问地址
web.xml中的配置信息:
-
<servlet>标签用来给Tomcat配置Servlet程序 <servlet-name>标签给Servlet程序起别名 <servlet-class>标签是Servlet程序的全类名
-
<servlet-mapping>标签给Servlet程序配置访问地址 <servlet-name>标签告诉浏览器,当前配置的地址是个哪个Servlet程序使用的(通常和<servlet>标签中<servlet-name>标签内容一样) <url-pattern>标签配置访问地址(格式:/ + 访问地址)
3. Servlet程序的生命周期
1)执行构造器方法
2)执行 init 初始化方法
3)执行 service 方法
4)执行 destroy 销毁方法
程序演示:启动服务器后,访问配置的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 void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3. service方法 === 访问:Hello Servlet!");
}
@Override
public void destroy() {
System.out.println("4. 销毁方法");
}
}
注意:在执行顺序中,
1)2)在第一次访问的时候,创建servlet程序会调用
3)每次访问都会调用
4)在web工程停止的时候,才会执行
4. Get/Post请求的分发处理
1)通过 form 标签的 method 属性,表单的请求方式
<!-- 请求方法为POST -->
<form action="http://localhost:8080/06_Servlet/hello" method="post">
</form>
2)在Servlet程序的service方法中,获取请求类型
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 类型转换为HttpServletRequest,因为HttpServletRequest中有获取请求类型的方法
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 获取请求方法
String method = httpServletRequest.getMethod();
}
3)判断请求类型类别,执行不同的程序
// 判断请求类型,执行不同操作
if ("GET".equals(method)) {
doGet();
} else if ("POST".equals(method)) {
doPost();
}
4)doGet方法、doPost方法,减少service方法中的代码量
// 请求方法为Get时,执行的操作
public void doGet() {
System.out.println("Get方法");
}
// 请求方法为Post时,执行的操作
public void doPost() {
System.out.println("Post方法");
}
5. 继承HttpServlet实现Servlet程序
在实际开发中,其实很少通过以上实现Servlet接口的方式去实现Servlet程序,而是使用继承HttpServlet类的方式去实现Servlet程序 。
1)编写一个类继承HttpServlet
public class HelloServlet2 extends HttpServlet {
}
2)根据业务需要,重写doGet或doPost方法,HttpServlet类中已经分发好了get和post请求,会自动调用相对应的方法
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doGet方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doPost方法");
}
}
3)到web.xml中配置Servlet程序的访问地址
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.ldyr.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
6. Servlet类的继承体系
二、ServletConfig类
1. 说明
1)ServletConfig 类 ,用来给Servlet 程序配置初始化信息
2)Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用
3)Servlet 程序默认在第一次访问配置的地址的时候创建,ServletConfig 对象是每个 Servlet 程序创建时,就会创建一个对应的 ServletConfig 对象
4)重写 init 初始化方法时,要在重写的方法中加上 super.init(ServletConfig实例对象),用来重新调用父类中的 init 方法,否则父类中的保存操作就会丢失,随之ServletConfig类 获取初始信息会出现问题
2. ServletConfig类三大作用
1).getServletName(),可以获取Servlet程序的别名,也就是 servlet-name 标签的值
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//1)可以获取Servlet程序的别名,也就是 servlet-name 标签的值
System.out.println("HelloServlet程序的别名是:" + servletConfig.getServletName());
}
2).getInitParameter(),可以获取初始化参数 init-param,需要在web.xml中配置初始化参数 init-param(键值对的形式)
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//2)可以获取初始化参数 init-param
System.out.println("HelloServlet程序的初始化参数是:" + servletConfig.getInitParameter("username"));
System.out.println("HelloServlet程序的初始化参数是:" + servletConfig.getInitParameter("password"));
System.out.println("HelloServlet程序的初始化参数是:" + servletConfig.getInitParameter("url"));
}
<servlet>
<!-- 配置HelloServlet程序的初始化参数-->
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
</servlet>
3).getServletContext(),可以获取 ServletContext 对象
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//3)可以获取 ServletContext 对象
System.out.println("HelloServlet程序的ServletContext对象是:" + servletConfig.getServletContext());
}
三、ServletContext类
1. 说明
1)ServletContext 类是一个接口,它表示 Servlet 上下文对象
2)一个 web 工程只有一个 ServletContext 对象
3)ServletContext 对象是域对象:
i)域对象是可以向 Map 一样存取数据的对象
ii))这里的域指的是存取数据的范围,这个范围是整个 web 工程
4)Map 和域对象区别
存数据 取数据 删除数据
Map put() get() remove()
域对象 setAttribute() getAttribute() removeAttribute()
5)ServletContext 在 web 工程部署启动的时候创建,在 web 工程停止的时候销毁
2. ServletContext类的四个作用
1).getInitParameter(),获取 web.xml 中配置的上下文参数 context-param,需要在 web.xml 中配置上下文参数context-param(键值对形式,属于整个 web 工程)
public class ServletContext extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
// 1)获取 web.xml 中配置的上下文参数 context-param
System.out.println("获取上下文参数username:" + servletContext.getInitParameter("username"));
}
}
<!-- 配置ServletContext程序的上下文参数-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>
2).getContextPath(),获取当前的工程路径
public class ServletContext extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
// 2)获取当前的工程路径
System.out.println("当前的工程路径是:" + servletContext.getContextPath());
}
}
3).getRealPath(),获取工程部署后在服务器磁盘上的绝对路径
public class ServletContext extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
// 3)获取工程部署后在服务器磁盘上的绝对路径
// "/" 被浏览器解析为:http://ip:port/工程名/,相当于映射到IDEA代码的web目录
System.out.println("工程部署的绝对路径是:" + servletContext.getRealPath("/"));
System.out.println("WEB-INF的绝对路径是:" + servletContext.getRealPath("/WEB-INF"));
}
}
4)像Map一样存取数据
public class ServletContext extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
// 4)像Map一样存取数据
System.out.println("数据储存前的key1:" + servletContext.getAttribute("key1"));
// key1 = value1
servletContext.setAttribute("key1","value1");
System.out.println("数据储存后的key1:" + servletContext.getAttribute("key1"));
}
}
5)注意:只要有 ServletContext 对象,同个工程下,不管在哪个 Servlet 程序中,都可以取到 ServletContext 对象中保存过的数据,不存在或没保存这个数据的话会返回null
public class ServletContext extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
ServletContext servletContext = getServletContext();
// 获取 ServletContext 对象中保存过的数据key1的值
System.out.println("ServletContext对象的key1的值是:" + servletContext.getAttribute("key1"));
}
}
四、Http 协议
1. 什么是 Http 协议
1)所谓 Http 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫Http协议
2)Http 协议中的数据,又称为报文
2. 请求的 Http 协议格式
1)客户端给服务器发送的数据叫请求
2)服务器给客户端回传的数据叫响应
3)请求又分为 GET 请求和 POST 请求
i)GET 请求
1.请求行
1)请求的方式 GET
2)请求的资源路径 [+?+请求参数]
3)请求的协议和版本号 HTTP/1.1
2.请求头
key:value 组成,不同的键值对,表示不同的意义
3.以下为 GET 请求的 HTTP 协议内容:
ii)POST请求
1.请求行
1)请求的方式 POST
2)请求的资源路径 [+?+请求参数]
3)请求的协议和版本号 HTTP/1.1
2.请求头
1)key:value 组成,不同的键值对,表示不同的意义
2)请求头和请求体之间,有一个空行
3.请求体 ===>>> 发送给服务器的内容(用户输入的或选择的)
4.以下是POST请求的HTTP协议内容:
4)常用请求头说明
1. Accept:客户端可以接受的信息
2. Accept-Language:客户端可以接受的语言类型
3. User-Agent:浏览器的信息
4. Host:请求时服务器 ip 和端口号
5)区分 GET 请求和 POST 请求
GET 请求有哪些?
1. form标签 method=get
2. a标签引入链接
3. img标签引入图片
4. iframe标签引入 html 页面窗口
5. link标签引入 css 文件
6. script标签引入 js 文件
7. 浏览器地址栏敲入地址后回车
POST 请求有哪些?
8. form标签 method=POST
3. 响应的 Http 协议格式
1)响应行
1. 响应的协议和版本号
2. 响应的状态码
3. 响应的状态描述
2)响应头
1. key:value,不同的响应头,有不同的含义
2. 响应头和响应体之间,有一个空行
3)响应体 ===>>> 回传给客户端的数据
4)以下为响应的 Http 协议内容:
4. 常用响应码说明
1)200:表示请求成功
2)302:表示请求重定向
3)404:表示请求服务器已经收到,但是需要的数据不存在(通常是请求地址错误)
4)500:表示服务器已经收到请求,但是服务器内部(代码)错误
5. MIME 类型说明
1)MIME 是 Http 协议中的数据类型
2)常见的 MIME 类型:
五、HttpServletRequest 类
1. HttpServletRequest 类的作用
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 Http 协议信息解析好封装到 Request 对象中,然后传递到 service 方法(doGet/doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。
2. HttpServletRequest类的常用 API
1)getRequestURI():获取请求的资源路径
2)getRequestURL():获取请求的资源的绝对路径
3)getRemoteHost():获取客户端的 IP 地址
4)getHeader():获取请求头
5)getParameter():获取请求的参数
6)getParameterValues():获取请求的参数(多个值的时候使用)
7)getMethod():获取请求的方式 Get/Post
8)setAttribute(key,value):设置域数据
9)getAttribute(key):获取域数据
10)getRequestDispatcher():获取请求转发对象
代码演示:
public class RequestAPIServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1)getRequestURI():获取请求的资源路径
System.out.println("URI:" + request.getRequestURI());
// 2)getRequestURL():获取请求的资源的绝对路径
System.out.println("URL:" + request.getRequestURL());
// 3)getRemoteHost():获取客户端的 IP 地址
System.out.println("客户端IP地址:" + request.getRemoteHost());
// 4)getHeader():获取请求头
System.out.println("请求头Accept:" + request.getHeader("Accept"));
// 7)getMethod():获取请求的方式 Get/Post
System.out.println("Method:" + request.getMethod());
}
}
3. 获取客户端请求的参数:getParameter()
1)新建一个表单页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="get" action="http://localhost:8080/06_Servlet/paramter">
<label>用户名:</label><input type="text" name="username"><br>
<label>密码:</label><input type="password" name="password"><br>
<label>用户名</label><input type="checkbox" name="hobby" value="C">C
<input type="checkbox" name="hobby" value="Java">Java
<input type="checkbox" name="hobby" value="Python">Python<br>
<input type="submit">
</form>
</body>
</html>
2)获取表单中的信息
public class ParameterServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 5)getParameter():获取请求的参数
// 6)getParameterValues():获取请求的参数(多个值的时候使用)
String username = request.getParameter("username");
String password = request.getParameter("password");
String[] hobby = request.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("爱好:" + Arrays.asList(hobby));
}
}
3)Post请求中文乱问题
public class ParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 5)getParameter():获取请求的参数
// 6)getParameterValues():获取请求的参数(多个值的时候使用)
System.out.println("-------------doGet--------------");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("爱好:" + Arrays.asList(hobby));
}
}
4)解决中文乱码的方法:可以通过设置请求体的字符集为 UTF-8,解决此问题,在上述 doPost 方法中加入以下代码。注意,.setCharacterEncoding()这个API需要在所有的获取请求参数前调用才有效。
// 设置请求体的字符集为UTF-8,解决Post请求的中文乱码问题
// .setCharacterEncoding()这个API需要在所有的获取请求参数前调用才有效
req.setCharacterEncoding("UTF-8");
4. 请求转发:getRequestDispatcher()
- 请求的转发,是指服务器收到请求后,从一个资源跳转到另一个资源的操作叫请求转发
1)资源1:Servlet1执行操作,并转发请求:getRequestDispatcher(path)
public class Servlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("===========Servlet1业务=============");
// 获取请求的参数
String username = request.getParameter("username");
System.out.println("获取的请求参数用户名是:" + username);
// 设置域数据
request.setAttribute("key1","Servlet1_pass");
// 设置请求转发到??地址
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
// 设置跳转到 /servlet2
requestDispatcher.forward(request,response);
}
}
2)资源2:Servlet2执行业务
public class Servlet2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("===========Servlet2业务=============");
// 获取请求的参数
String username = request.getParameter("username");
System.out.println("获取的请求参数用户名是:" + username);
// 查看请求转发前的域数据
Object key1 = request.getAttribute("key1");
System.out.println("请求转发前的数据key1:" + key1);
// 处理业务
System.out.println("Servlet2_pass");
}
}
3)请求转发的特点
1. 浏览器地址栏没有变化
2. 请求转发只有一次请求
3. 请求共享 Request 域中的数据
4. 请求可以转发到 WEB-INF 目录下
5. 不可以访问本工程以外的资源
5. Base标签
作用:base 标签可以设置当前页面中所有相对路径工作时,参照哪个路径进行跳转
代码演示:通过请求转发实现从 index.html 跳转到 /a/b/c.html 页面
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
首页<br/>
<a href="a/b/c.html">跳转到a/b/c.html</a><br>
<a href="http://localhost:8080/06_Servlet/forwordC">请求转发:/a/b/c.html</a>
</body>
</html>
c.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>c.html</title>
<!--base标签设置本页所有相对路径工作时,
参照href属性中的路径进行跳转-->
<base href="http://localhost:8080/06_Servlet/a/b/c.html">
</head>
<body>
a下的b下的c.html<br/>
<a href="../../index.html">跳转到首页</a>
</body>
</html>
实现请求转发的Servlet程序:
public class ForwordC extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求转发到a/b/c.html");
// 请求转发到a/b/c.html页面
request.getRequestDispatcher("a/b/c.html").forward(request,response);
}
}
六、HttpServletResponse 类
1. HttpServletResponse 类的作用
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。
2. 两个响应流说明
1)字节流:getOutputStream(); 常用于下载(传递二进制数据)
2)字符流:getWriter(); 常用于回传字符串(常用)
3)特点:字节流和字符流同时只能使用一个,否则就会报错
3. 回传给客户端数据
1)getWriter.writer(),往客户端回传 字符串 数据
public class ResponseIOServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 往客户端回传 字符串 数据
PrintWriter writer = response.getWriter();
writer.write("Response content");
}
}
2)若回传字符串为中文,则会出现乱码问题
public class ResponseIOServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 往客户端回传 字符串 数据
PrintWriter writer = response.getWriter();
writer.println("Response content");
// 若回传为中文,则会出现中文乱码的问题
writer.println("回传字符串数据");
}
}
3)解决中文乱码的两种方法
i. 可以通过设置请求体的字符集为 UTF-8,解决此问题,但是浏览器的字符集为 GBK,所有还要通过设置响应头,来设置浏览器字符集为 UTF-8。
// 解决中文乱码的方法一:设置请求体的字符集为UTF-8,解决回传数据的中文乱码问题
// .setCharacterEncoding()这个API需要在所有的获取请求参数前调用才有效
response.setCharacterEncoding("UTF-8");
// 但是浏览器的字符集为 GBK,所以需要设置浏览器的响应字符集为U8
response.setHeader("Content-type","text/html; charset=UTF-8");
ii. setContentType(),同时设置服务器和浏览器的字符集为 UTF-8。注意:此方法一定要在获取流对象之前使用才有效。
// 解决中文乱码的方法二:同时设置服务器和浏览器的字符集为u8
response.setContentType("tetx/html; charset=UTF-8");
4. 请求重定向
1)说明:请求重定向,是指客户端发请求给服务器,服务器告诉客户端一个新地址去访问,叫做请求重定向(重定向的原因:可能之前的地址已经被废弃)。
2)重定向步骤:
1. 在被弃用的 Servlet 程序中,
i. setStatus(),设置状态响应码 302 --- 表示重定向
public class Response1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Response1 已被弃用,重定向至 Response2");
// 设置状态响应码:302,表示重定向
response.setStatus(302);
}
}
ii. setHeader(),设置响应头中心的地址 (Location,path),说明新的地址在哪里
public class Response1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应头中心的地址 (Location,path),说明新的地址在哪里
response.setHeader("Location","http://localhost:8080/06_Servlet/response2");
}
}
2. 在新的 Servlet 程序中,执行新的业务
public class Response2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("已经重定向至 Response2");
// 响应请求
response.getWriter().write("response2 content");
}
}
3)请求重定向的特点
1. 浏览器地址会发生变化
2. 两次请求
3. 不共享Request域中的数据,因为每次请求就会把请求过来的数据解析好封装成一个Request对象,两次请求不是一个Request对象
4. 不能访问 WEB-INF 下的资源
5. 可以访问本工程以外的资源
5. 请求重定向的方法二(推荐使用)
- sendRedirect(),直接告诉重定向地址,由于 响应状态时固定的302,所有不需要设置
public class Response1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 请求重定向的方法二:推荐使用
response.sendRedirect("http://localhost:8080/06_Servlet/response2");
}
}