Servlet
1.Servlet
Servlet 是 JavaWeb 三大组件之一。 三大组件分别是: Servlet 程序、 Filter 过滤器、 Listener 监听器.可以接收客户端发送过来的请求, 并响应数据给客户端。
2.获取参数
首先要加入Tomcat依赖
idea过程:Project Structure -->Modules --> 当前项目 --> Dependencies --> + -->lib -->Application Server Libraries -->Tomcat
//以水果商城为例
//要继承HttpServlet
public class AddServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
System.out.println("fname = " + fname);
System.out.println("price = " + price);
System.out.println("fcount = " + fcount);
System.out.println("remark = " + remark);
//添加进数据库,加上JDBC的代码可以将数据放入数据库中
FruitDAO fruitDAO = new FruitDAOImpl();
boolean flag = fruitDAO.addFruit(new Fruit(0, fname, price, fcount, remark));
System.out.println(flag? "添加成功": "添加失败");
}
}
add.html的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add" method="post">
名称: <input type="text" name="fname"/><br/>
价格: <input type="text" name="price"/><br/>
库存: <input type="text" name="fcount"/><br/>
备注: <input type="text" name="remark"/><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
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-name>AddServlet</servlet-name>
<!--servlet-class 是 Servlet 程序的全类名-->
<servlet-class>com.serein.servlets.AddServlet</servlet-class>
</servlet>
<!--servlet-class 是 Servlet 程序的全类名-->
<servlet-mapping>
<!--servlet-name 标签的作用是告诉服务器, 我当前配置的地址给哪个 Servlet 程序使用-->
<servlet-name>AddServlet</servlet-name>
<!--url-pattern 标签配置访问地址 <br/>
/ 斜杠在服务器解析的时候, 表示地址为: http://ip:port/工程路径 <br/>
/add 表示地址为: http://ip:port/工程路径/add <br/>
-->
<url-pattern>/add</url-pattern>
</servlet-mapping>
</web-app>
url地址访问到Servlet程序的过程:
①用户发请求,action=add
②项目中,web.xml中找到url-pattern = /add -> 第12行
③找第11行的servlet-name = AddServlet
④找和servlet-mapping中servlet-name一致的servlet , 找到第7行
⑤找第8行的servlet-class -> com.atguigu.servlets.AddServlet
⑥用户发送的是post请求(method=post) , 因此 tomcat会执行AddServlet中的doPost方法
3.设置编码
注意:设置编码代码需在所有的获取参数之前
(1)get请求
基于Tomcat8下,get方式下目前不需要设置编码
在Tomcat之前,get请求发送的中文数据,转码如下
String fname = request.getParameter("fname");
//1.将字符串打散成字节数组
byte[] bytes = fname.getBytes("ISO-8859-1");
//2.将字节数组按照设定的编码重新组装成字符串
fname = new String(bytes,"UTF-8");
(2)post请求
request.setCharacterEncoding("UTF-8");
4.Servlet的继承关系
–javax.servlet.Servlet接口
— javax.servlet.GenericServlet抽象类
---- javax.servlet.http.HttpServlet抽象子类
5.Servlet的相关方法
javax.servlet.Servlet接口:
void init(config) - 初始化方法
void service(request,response) - 服务方法
void destory() - 销毁方法
javax.servlet.GenericServlet抽象类:
void service(request,response) - 仍然是抽象的
javax.servlet.http.HttpServlet 抽象子类:
void service(request,response) - 不是抽象的
1. String method = req.getMethod(); 获取请求的方式
2. 各种if判断,根据请求方式不同,决定去调用不同的do方法
if (method.equals("GET")) {
this.doGet(req,resp);
} else if (method.equals("HEAD")) {
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
3. 在HttpServlet这个抽象类中,do方法都差不多:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
服务方法: 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)。
在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等。
然后再决定调用的是哪个do开头的方法,那么在HttpServlet中这些do方法默认都是405的实现风格-
要我们子类去实现对应的方法,否则默认会报405错误。因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法
建议表单使用post的方法,剩下的方法使用get方法
6.Servlet的生命周期
(1)生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy()
(2)默认情况下:
第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
从第二次请求开始,每一次都是服务。当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法
(3)Servlet的初始化时机:
默认是第一次接收请求时,实例化,初始化
我们可以通过来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0
例:
<servlet>
<servlet-name>Demo02Servlet</servlet-name>
<servlet-class>Demo02Servlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
(4)Servlet在容器中是:单例的、线程不安全的
单例:所有的请求都是同一个实例去响应
线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
7.Http协议
(1)Http 称之为 超文本传输协议
(2) Http是无状态的
(3) Http请求响应包含两个部分:请求和响应
-
请求:
-
请求包含三个部分:1.请求行;2.请求消息头;3.请求主体
-
请求行包含了三个信息:1.请求的方式;2.请求的URL;3.请求的协议(HTTP协议的版本,一般都是HTTP1.1)
-
请求消息头包含了客户端需要告诉服务器的信息
- 相对比较重要的请求消息头:
名称 功能 Host 服务器的主机地址 Accept 声明当前请求能够接受的『媒体类型』 Referer 当前请求来源页面的地址 Content-Length 请求体内容的长度 Content-Type 请求体的内容类型,这一项的具体值是媒体类型中的某一种 Cookie 浏览器访问服务器时携带的Cookie数据 -
请求体,三种情况:
- get方式,没有请求体,但是有一个queryString
- post方式,有请求体,form data
- json方式,有请求体,request payload
-
-
响应:
-
响应也包含三部分:1.响应状态行;2.响应消息头;3.响应头
-
响应状态行:包含了三个信息:1.协议;2.响应状态码(200)3.响应状态(ok)
-
响应消息头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
-
名称 功能 Content-Type 响应体的内容类型 Content-Length 响应体的内容长度 Set-Cookie 服务器返回新的Cookie信息给浏览器 location 在重定向的情况下,告诉浏览器访问下一个资源的地址
-
-
响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是…)
-
8.会话
(1)Http是无状态的
- HTTP无状态:服务器无法判断这两次请求时同一个客户端发过来的,还是不同的客户端发过来的
- 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求时结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱。
- 通过会话跟踪问题来解决无状态问题
(2)会话跟踪技术
-
客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
-
下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次的请求和上一次的请求是同一个客户端,从而能够区分客户端
-
常用的API:
-
request.getSession() --> 获取当前的会话,没有则创建一个新的会话
-
request.getSession(true) --> 效果和不带参数一致
-
request.getSession(false) --> 获取当前会话,没有则返回null,不会创建新的
-
session.getId() --> 获取sessionID
-
session.isNew() --> 判断当前session是否是新的
-
session.getMaxInactiveInterval() --> session的非激活间隔时长,默认1800秒
-
session.setMaxInactiveInterval() -->设置Session最大的过期时间
-
session.invalidate() --> 强制性让会话立即失效
-
(3)session保存作用域
- session保存作用域是和具体的某一个session对应的
- 常用的API:
- void session.setAttribute(k,v) --> 设置
- Object session.getAttribute(k) --> 获取
- void removeAttribute(k) -->删除
9.服务器内部转发以及客户端重定向
(1)服务器内部转发:request.getRequestDispatcher("…").forward(request,response);
- 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
- 地址栏没有变化
(2)客户端重定向:response.sendRedirect("…");
- 两次请求响应的过程.客户端是知道请求URL有变化
- 地址栏有变化
10.保存作用域
原始情况下,保存作用域我们可以认为有四个: page(页面级别,现在几乎不用) , request(一次请求响应范围) , session(一次会话范围) , application(整个应用程序范围)
1) request:一次请求响应范围
@WebServlet("/demo01")
public class demo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.向request保存作用域保存数据
request.setAttribute("uname","lili");
//2.客户端重定向
// response.sendRedirect("demo02");
//3.服务器内部转发
request.getRequestDispatcher("demo02").forward(request,response);
}
}
@WebServlet("/demo02")
public class demo02 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取request保存作用域保存的数据,key为uname
Object uname = request.getAttribute("uname");
System.out.println("uname = " + uname);
}
}
在客户端重定向内,demo02无法获取带uname的值,但服务器内部转发可以获取到uname的值
2) session:一次会话范围有效
只要sessionID没有过期就可获取到uname的值
3) application: 一次应用程序范围有效