1.执行流程
1.浏览器发出http://localhost:8080/web-demo/demo1
请求,从请求中可以解析出三部分内容,分别是localhost:8080
、web-demo
、demo1
- 根据
localhost:8080
可以找到要访问的Tomcat Web服务器 - 根据
web-demo
可以找到部署在Tomcat服务器上的web-demo项目 - 根据
demo1
可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配
2.找到ServletDemo1这个类后,Tomcat Web服务器就会为ServletDemo1这个类创建一个对象,然后调用对象中的service方法
- ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用
- service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互
2.生命周期
- 生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。
1.加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
2.初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次
3.请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的==service()方法对请求进行处理
4.服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()==方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
@WebServlet(urlPatterns = "/demo2",loadOnStartup = 1)
初始化方法
* 1.调用时机:默认情况下,Servlet被第一次访问时,调用
* loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用,数字越小优先级越高.
* 2.调用次数: 1次
init
* 初始化方法
* 1.调用时机:默认情况下,Servlet被第一次访问时,调用
* * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用
* 2.调用次数: 1次
* @param config
* @throws ServletException
service
/**
* 提供服务
* 1.调用时机:每一次Servlet被访问时,调用
* 2.调用次数: 多次
* @param req
* @param res
* @throws ServletException
* @throws IOException
*/
destroy
/**
* 销毁方法
* 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用
* 2.调用次数: 1次
*/
3.体系结构
在HttpServlet中我们一般封装了一些方法,变量和常量 当发送不同类型的请求时-Post或者Get,我们一般需要判断来获得其中的参数等等内容,这个HttpServlet这个类进行了封装,我们只需要重写doGet和doPost两个方法即可。其中的判断继承的HttpServlet已经帮我们写好了。
- HttpServlet的使用步骤
继承HttpServlet
重写doGet和doPost方法
- HttpServlet原理
获取请求方式,并根据不同的请求方式,调用不同的doXxx方法
视频:96 --Javaweb
4.urlPattern
1.精确匹配
@WebServlet(urlPatterns={“/demo7”,“/demo8”})
2.目录匹配
3.扩展名匹配
4.任意匹配
注意:/
和/*
的区别?
- 当我们的项目中的Servlet配置了 “/”,会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet
- 当我们的项目中配置了"/*",意味着匹配任意访问路径
- DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问
小结
- urlPattern总共有四种配置方式,分别是精确匹配、目录匹配、扩展名匹配、任意匹配
- 五种配置的优先级为 精确匹配 > 目录匹配> 扩展名匹配 > /* > / ,无需记,以最终运行结果为准。
5.XML配置方式编写Servlet
前面对应Servlet的配置,我们都使用的是@WebServlet,这个是Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。
对于XML的配置步骤有两步:
- 编写Servlet类
- 在web.xml中配置该Servlet
<?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 全类名
-->
<servlet>
<!-- servlet的名称,名字任意-->
<servlet-name>demo13</servlet-name>
<!--servlet的类全名-->
<servlet-class>com.itheima.web.ServletDemo13</servlet-class>
</servlet>
<!--
Servlet 访问路径
-->
<servlet-mapping>
<!-- servlet的名称,要和上面的名称一致-->
<servlet-name>demo13</servlet-name>
<!-- servlet的访问路径-->
<url-pattern>/demo13</url-pattern>
</servlet-mapping>
</web-app>
5.Request和Response
1.Request
1.Request继承体系
Request的继承体系
- Request的继承体系为ServletRequest–>HttpServletRequest–>RequestFacade
- Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法
- 使用request对象,可以查阅JavaEE API文档的HttpServletRequest接口中方法说明
2.Request获取请求数据
1.请求行包含三块内容,分别是请求方式
、请求资源路径
、HTTP协议及版本
- 获取请求方式:
GET
String getMethod()
- 获取虚拟目录(项目访问路径):
/request-demo
String getContextPath()
- 获取URL(统一资源定位符):
http://localhost:8080/request-demo/req1
StringBuffer getRequestURL()
- 获取URI(统一资源标识符):
/request-demo/req1
String getRequestURI()
- 获取请求参数(GET方式):
username=zhangsan&password=123
String getQueryString()
2.获取请求体数据
- getReader() ------------ 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法
BufferedReader getReader()
BufferedReader reader = req.getReader();
String s = reader.readLine();
System.out.println(s);
- getInputStream()—获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法
ServletInputStream getInputStream()
该方法可以获取字节
3.请求头数据
String getHeader(String name)
//获取请求头: user-agent: 浏览器的版本信息
String agent = req.getHeader("user-agent");
System.out.println(agent);
3.Request获取请求参数的通用方式
-----get和Post参数获取的通用方法。
//@WebServlet(urlPatterns = "*.do")
public class Servletdemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp); //核心
}
}
- 获取所有参数Map集合
Map<String,String[]> getParameterMap()
- 根据名称获取参数值(数组)
String[] getParameterValues(String name)
- 根据名称获取参数值(单个值)
String getParameter(String name)
//获取所有参数的Map集合
Map<String, String[]> parameterMap = req.getParameterMap();
for (String s : parameterMap.keySet()) {
System.out.print(s+':');
String[] strings = parameterMap.get(s);
for (String string : strings) {
System.out.print(string);
}
System.out.println();
}
//根据名称获取参数值(数组)
String[] usernames = req.getParameterValues("username");
for (String username : usernames) {
System.out.println(username);
}
//根据名称获取参数值(单个值,会获得第一个)
String username = req.getParameter("username");
System.out.println(username);
4.IDEA快速创建Servlet
1.按照自己的需求,修改Servlet创建的模板内容
2.创建Servlet项目
5.Request解决请求参数中文乱码问题
1.POST
分析出现中文乱码的原因:
- POST的请求参数是通过request的getReader()来获取流中的数据
- TOMCAT在获取流的时候采用的编码是ISO-8859-1
- ISO-8859-1编码是不支持中文的,所以会出现乱码
解决方案:
- 页面设置的编码格式为UTF-8
- 把TOMCAT在获取流数据之前的编码设置为UTF-8
- 通过request.setCharacterEncoding(“UTF-8”)设置编码,UTF-8也可以写成小写
2.Get
URL编码:
这块知识我们只需要了解下即可,具体编码过程分两步,分别是:
(1)将字符串按照编码方式转为二进制
(2)每个字节转为2个16进制数并在前边加上%
张三
按照UTF-8的方式转换成二进制的结果为:
1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001
String username = "张三";
//1. URL编码
String encode = URLEncoder.encode(username, "utf-8");
System.out.println(encode); //打印:%E5%BC%A0%E4%B8%89
//2. URL解码
//String decode = URLDecoder.decode(encode, "utf-8");//打印:张三
String decode = URLDecoder.decode(encode, "ISO-8859-1");//打印:`å¼ ä¸ `
System.out.println(decode);
GET请求中文参数出现乱码的原因
- 浏览器把中文参数按照
UTF-8
进行URL编码 - Tomcat对获取到的内容进行了
ISO-8859-1
的URL解码 - 在控制台就会出现类上
å¼ ä¸‰
的乱码,最后一位是个空格
解决方案:
1.按照ISO-8859-1编码获取乱码
å¼ ä¸‰
对应的字节数组2.按照UTF-8编码获取字节数组对应的字符串
String username = request.getParameter("username");
byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
String s = new String(bytes, StandardCharsets.UTF_8);
System.out.println(s);
4.Request请求转发
1.请求转发(forward):一种在服务器内部的资源跳转方式。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫请求转发
2.请求转发的实现方式:
req.getRequestDispatcher(“资源B路径”).forward(req,resp);
3.请求转发资源间共享数据:使用Request对象
- 1.存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
request.setAttribute(“name”,“hello”);
- 2.根据key获取值
Object getAttribute(String name);
- 3.根据key删除该键值对
void removeAttribute(String name);
2.Response
体系结构
1.Respones请求重定向
Response重定向(redirect):一种资源跳转方式。
1.响应行
void setStatus(int sc);
一般设置响应状态码。
2.响应头
void setHeader(String name,String value);
3.响应体
获取字符输出流: PrintWriter getWriter();
获取字节输出流:ServletOutputStream getOutputStream();
//重定向
//1.设置响应状态码 302
response.setStatus(302);
//设置响应头 Location
response.setHeader("Location","/request-demo/resp2");
//2.简化版本
resposne.sendRedirect("/request-demo/resp2")
重定向和转发的区别
重定向的实现方式:
resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");
---重定向的简化方式
resposne.sendRedirect("/request-demo/resp2");
2.路径问题
判断依据:
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录
- <a href='路径'>` 超链接,从浏览器发送,需要加
- `<form action='路径'>` 表单,从浏览器发送,需要加
- req.getRequestDispatcher("路径") 转发,是从服务器内部跳转,不需要加
- resp.sendRedirect("路径") 重定向,是由浏览器进行跳转,需要加
//简化方式完成重定向
//动态获取虚拟目录
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/resp2");
3.Response响应字符数据和字节数据
1.响应字符数据
一般为文字等等
- 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
- 通过字符输出流写数据: writer.write(“aaa”);
1.返回一串html字符串,并且能被浏览器解析 并且能够解析中文
response.setContentType(“text/html;charset=utf-8”);
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("哈哈");
writer.write("<h1>哈哈</h1>");
中文乱码问题:
response.setContentType("text/html;charset=utf-8");
2.响应字节数据
为文件和图片等等
- 通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
- 通过字节输出流写数据: outputStream.write(字节数据);
//1. 读取文件
FileInputStream fis = new FileInputStream("d://a.jpg");
//2. 获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!= -1){
os.write(buff,0,len);
}
fis.close();
对于流的copy的代码还是比较复杂的,所以我们可以使用别人提供好的方法来简化代码的开发,具体的步骤是:
1.pom.xml添加依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2.调用工具类方法
//fis:输入流
//os:输出流
IOUtils.copy(fis,os);
实现代码
FileInputStream fis = new FileInputStream("C://Users//Administrator//Desktop//aaa.jpg");
ServletOutputStream os = response.getOutputStream();
IOUtils.copy(fis,os);
6.JSP
初始开发环境
- 在
dependencies
标签中导入 JSP 的依赖,如下
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
- 在项目的
webapp
下创建jsp页面
1.JSP 原理
JSP 本质上就是一个 Servlet , 其中html的代码转换成如下形式。
而Java代码直接显示。
2.JSP 脚本
JSP 脚本有如下三个分类:
- <%…%>:内容会直接放到_jspService()方法之中
<%
System.out.println(“hello,jsp~”);
int i = 3;
%>
- <%=…%>:内容会放到out.print()中,作为out.print()的参数
<%=“hello”%>
<%=i%>
- <%!…%>:内容会放到_jspService()方法之外,被类直接包含
<%!
void show(){}
String name = “zhangsan”;
%>
<%@ page import="com.itheima.pojo.Brand" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 查询数据库
List<Brand> brands = new ArrayList<Brand>();
brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="新增"><br>
<hr>
<table border="1" cellspacing="0" width="800">
<tr>
<th>序号</th>
<th>品牌名称</th>
<th>企业名称</th>
<th>排序</th>
<th>品牌介绍</th>
<th>状态</th>
<th>操作</th>
</tr>
<%
for (int i = 0; i < brands.size(); i++) {
Brand brand = brands.get(i);
%>
<tr align="center">
<td><%=brand.getId()%></td>
<td><%=brand.getBrandName()%></td>
<td><%=brand.getCompanyName()%></td>
<td><%=brand.getOrdered()%></td>
<td><%=brand.getDescription()%></td>
<td><%=brand.getStatus() == 1 ? "启用":"禁用"%></td>
<td><a href="#">修改</a> <a href="#">删除</a></td>
</tr>
<%
}
%>
</table>
</body>
</html>
3.JSP缺点
由于 JSP页面内,既可以定义 HTML 标签,又可以定义 Java代码,造成了以下问题:
-
书写麻烦:特别是复杂的页面,既要写 HTML 标签,还要写 Java 代码
-
阅读麻烦,上面案例的代码,相信你后期再看这段代码时还需要花费很长的时间去梳理
-
复杂度高:运行需要依赖于各种环境,JRE,JSP容器,JavaEE…
-
占内存和磁盘:JSP会自动生成.java和.class文件占磁盘,运行的是.class文件占内存
-
调试困难:出错后,需要找到自动生成的.java文件进行调试
-
不利于团队协作:前端人员不会 Java,后端人员不精 HTML
如果页面布局发生变化,前端工程师对静态页面进行修改,然后再交给后端工程师,由后端工程师再将该页面改为 JSP 页面
4.EL 表达式
EL(全称Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。
EL 表达式的主要作用是 获取数据。其实就是从域对象中获取数据,然后将数据展示在页面上。
而 EL 表达式的语法也比较简单, ${expression}
。例如:${brands} 就是获取域中存储的 key 为 brands 的数据。
例如:
1. @WebServlet("/Servletdemo3")
//1. 准备数据
List<Brand> brands = new ArrayList<Brand>();
brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
request.setAttribute("brands",brands);
request.getRequestDispatcher("/hello.jsp").forward(request,response);
2.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>hello jsp</h1>
<%= "hello world"%>
${brands}
</body>
</html>
域对象
JavaWeb中有四大域对象,分别是:
- page:当前页面有效
- request:当前请求有效 (如上案例)
- session:当前会话有效
- application:当前应用有效
el 表达式获取数据,会依次从这4个域中寻找,直到找到为止。而这四个域对象的作用范围如下图所示
例如: ${brands},el 表达式获取数据,会先从page域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据。
5.JSTL标签
JSP标准标签库(Jsp Standarded Tag Library) ,使用标签取代JSP页面上的Java代码。如下代码就是JSTL标签
使用前提导入内容:
- 导入坐标
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
- 在JSP页面上引入JSTL标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
1.if 标签
<c:if test="${flag == 1}">
男
</c:if>
<c:if test="${flag == 2}">
女
</c:if>
2.forEach 标签
1.类似于 Java 中的增强for循环。涉及到的 <c:forEach>
中的属性如下
- items:被遍历的容器 ${对象}
- var:遍历产生的临时变量
- varStatus:遍历状态对象
从域对象中获取名为 brands 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 brand
,是 Brand对象。在循环里面使用 EL表达式获取每一个Brand对象的属性值
<c:forEach items="${brands}" var="brand" varStatus="status">
<tr align="center">
-- <%--其中这个brand.id为直接通过id-----Id +get 去寻找这个方法获得数据--%>---
<%-- <td>${brand.id}</td>--%>
<td>${status.count}</td> ------------获取从一到后面的数字
<td>${brand.brandName}</td>
<td>${brand.companyName}</td>
<td>${brand.ordered}</td>
<td>${brand.description}</td>
<c:if test="${brand.status == 1}">
<td>启用</td>
</c:if>
<c:if test="${brand.status != 1}">
<td>禁用</td>
</c:if>
<td><a href="#">修改</a> <a href="#">删除</a></td>
</tr>
</c:forEach>
2.类似于 Java 中的普通for循环。涉及到的 <c:forEach>
中的属性如下
- begin:开始数
- end:结束数
- step:步长
<c:forEach begin="0" end="10" step="1" var="i">
${i}
</c:forEach>
6.MVC模式和三层架构
1.MVC
MVC 是一种分层开发的模式,其中:
-
M:Model,业务模型,处理业务
-
V:View,视图,界面展示
-
C:Controller,控制器,处理请求,调用模型和视图
MVC 好处: -
职责单一,互不影响。每个角色做它自己的事,各司其职。
-
有利于分工协作。
-
有利于组件重用
2.三层架构
- 数据访问层:对数据库的CRUD基本操作
- 业务逻辑层:对业务逻辑进行封装,组合数据访问层层中基本功能,形成复杂的业务逻辑功能。例如
注册业务功能
,我们会先调用数据访问层
的selectByName()
方法判断该用户名是否存在,如果不存在再调用数据访问层
的insert()
方法进行数据的添加操作 - 表现层:接收请求,封装数据,调用业务逻辑层,响应数据
而整个流程是,浏览器发送请求,表现层的Servlet接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法调用数据访问层方法进行数据的操作,依次返回到serlvet,然后servlet将数据交由 JSP 进行展示。
三层架构的每一层都有特有的包名称:
- 表现层:
com.itheima.controller
或者com.itheima.web
- 业务逻辑层:
com.itheima.service
- 数据访问层:
com.itheima.dao
或者com.itheima.mapper
联系:
7.会话技术
3个会话。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 服务器会收到多个请求,这多个请求可能来自多个浏览器,如上图中的6个请求来自3个浏览器
- 服务器需要用来识别请求是否来自同一个浏览器
- 服务器用来识别浏览器的过程,这个过程就是会话跟踪
- 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据
1.Cookie
Cookie的操作主要分两大类,本别是发送Cookie和获取Cookie
1.发送Cookie
- 创建Cookie对象,并设置数据
Cookie cookie = new Cookie(“key”,“value”);
- 发送Cookie到客户端:使用response对象
response.addCookie(cookie);
2.获取Cookie
- 获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
- 遍历数组,获取每一个Cookie对象:for
- 使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();
//获取Cookie
//1. 获取Cookie数组
Cookie[] cookies = request.getCookies();
//2. 遍历数组
for (Cookie cookie : cookies) {
//3. 获取数据
String name = cookie.getName();
if("username".equals(name)){
String value = cookie.getValue();
System.out.println(name+":"+value);
break;
}
}
1.Cookie的原理分析
Cookie的实现原理是基于HTTP协议的,其中设计到HTTP协议中的两个请求头信息:
- 响应头:set-cookie
- 请求头: cookie
2.Cookie的使用细节
- 1.Cookie的存活时间
setMaxAge(int seconds)
参数值为:
1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁
3.零:删除对应Cookie
//发送Cookie
//1. 创建Cookie对象
Cookie cookie = new Cookie("username","zs");
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7); //易阅读,需程序计算
//cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算
//2. 发送Cookie,response
response.addCookie(cookie);
- 2.Cookie如何存储中文
技术:URL编码方式,所以如果需要存储中文,就需要进行转码
A发送Cookie
//发送Cookie
String value = "张三";
//对中文进行URL编码
value = URLEncoder.encode(value, "UTF-8");
System.out.println("存储数据:"+value);
//将编码后的值存入Cookie中
Cookie cookie = new Cookie("username",value);
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7);
//2. 发送Cookie,response
response.addCookie(cookie);
----------------------------------------------------------
B接受Cookie
//获取Cookie
//1. 获取Cookie数组
Cookie[] cookies = request.getCookies();
//2. 遍历数组
for (Cookie cookie : cookies) {
//3. 获取数据
String name = cookie.getName();
if("username".equals(name)){
String value = cookie.getValue();//获取的是URL编码后的值 %E5%BC%A0%E4%B8%89
//URL解码
value = URLDecoder.decode(value,"UTF-8");
System.out.println(name+":"+value);//value解码后为 张三
break;
}
}
2.Session
Session:服务端会话跟踪技术:将数据保存到服务端。
- Session是存储在服务端而Cookie是存储在客户端
- 存储在客户端的数据容易被窃取和截获,存在很多不安全的因素
- 存储在服务端的数据相比于客户端来说就更安全
在JavaEE中提供了HttpSession接口,来实现一次会话的多次请求之间数据共享功能。
- 1.获取Session对象,使用的是request对象
HttpSession session = request.getSession();
- 2.Session对象提供的功能:
1.存储数据到 session 域中
void setAttribute(String name, Object o)
2.根据 key,获取值
Object getAttribute(String name)
3.根据 key,删除该键值对
void removeAttribute(String name)
A: 存储数据
//存储到Session中
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 存储数据
session.setAttribute("username","zs");
B:获取数据
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println(username);
1.Session的原理分析
Session是基于Cookie实现的
1.Set-Cookie:JESSIONID=10
到响应头中,并响应给浏览器
2.浏览器接收到响应结果后,会把响应头中的coookie数据存储到浏览器的内存中
3.cookie: JESSIONID=10的格式添加到请求头中并发送给服务器Tomcat
4.从请求头中就读取cookie中的JSESSIONID值为10,然后就会到服务器内存中寻找id:10
的session对象,如果找到了,就直接返回该对象,如果没有则新创建一个session对象
5.关闭打开浏览器后,因为浏览器的cookie已被销毁,所以就没有JESSIONID的数据,服务端获取到的session就是一个全新的session对象
2.Session的细节
1.Session钝化与活化:
钝化:在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中
钝化的数据路径为:`项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser
活化:再次启动服务器后,从文件中加载数据到Session中
数据加载到Session中后,路径中的
SESSIONS.ser
文件会被删除掉
2.Session销毁
- 1.默认情况下,无操作,30分钟自动销毁
- 2.对于这个失效时间,是可以通过配置进行修改的
<?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_3_1.xsd"
version="3.1">
<session-config>
<session-timeout>100</session-timeout>
</session-config>
</web-app>
- 3.调用Session对象的invalidate()进行销毁
3.Cookie和Session的区别
- 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
8.Filter
Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
1.进行 Filter
开发分成以下三步实现
- 定义类,实现 Filter接口,并重写其所有方法
package com.itheima.web.filter;
import javax.servlet.*;
import java.io.IOException;
@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//放行前执行代码
filterChain.doFilter(servletRequest,servletResponse);
//放行后执行代码
}
@Override
public void destroy() {
}
}
1.Filter拦截路径配置
拦截路径有如下四种配置方式:
- 拦截具体的资源:/index.jsp:只有访问index.jsp时才会被拦截
- 目录拦截:/user/*:访问/user下的所有资源,都会被拦截
- 后缀名拦截:*.jsp:访问后缀名为jsp的资源,都会被拦截
- 拦截所有:/*:访问所有资源,都会被拦截
2. 过滤器链
执行过滤器的优先级是根据过滤器类名(字符串)的自然排序
例如:BFilterDemo
和
AFilterDemo。那一定是
AFilterDemo` 过滤器先执行
8.Listener监听器
-
Listener 表示监听器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
-
监听器可以监听就是在
application
,session
,request
三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。ServletContext
代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。
JavaWeb 提供了8个监听器:
实现流程:
- 定义一个类,实现
ServletContextListener
接口 - 重写所有的抽象方法
- 使用
@WebListener
进行配置
@WebListener
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//加载资源
System.out.println("ContextLoaderListener...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//释放资源
}
}
启动服务器,就可以在启动的日志信息中看到 contextInitialized()
方法输出的内容,同时也说明了 ServletContext
对象在服务器启动的时候被创建了。
8.Ajax
AJAX
(Asynchronous JavaScript And XML):异步的 JavaScript 和 XML。
1.AJAX 作用
有以下两方面:
-
.1. 与服务器进行数据交换:通过AJAX可以给服务器发送请求,服务器将数据直接响应回给浏览器。
-
.2.异步交互**:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用校验,等等…
2.同步和异步
-
同步发送请求过程如下
-
异步发送请求过程如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
<link href="css/register.css" rel="stylesheet">
</head>
<body>
<div class="form-div">
<div class="reg-content">
<h1>欢迎注册</h1>
<span>已有帐号?</span> <a href="login.html">登录</a>
</div>
<form id="reg-form" action="#" method="get">
<table>
<tr>
<td>用户名</td>
<td class="inputs">
<input name="username" type="text" id="username">
<br>
<span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span>
</td>
</tr>
<tr>
<td>密码</td>
<td class="inputs">
<input name="password" type="password" id="password">
<br>
<span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
</td>
</tr>
<tr>
<td>验证码</td>
<td class="inputs">
<input name="checkCode" type="text" id="checkCode">
<img src="imgs/a.jpg">
<a href="#" id="changeImg">看不清?</a>
</td>
</tr>
</table>
<div class="buttons">
<input value="注 册" type="submit" id="reg_btn">
</div>
<br class="clear">
</form>
</div>
<script>
document.getElementById("username").onblur=function (){
//2. 发送ajax请求
// 获取用户名的值
var username = this.value;
//2. 发送ajax请求
//2.1. 创建核心对象
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//2.2. 发送请求
xhttp.open("GET", "http://localhost:8080/brand_demo/selectUserServlet?username="+username);
xhttp.send();
//2.3. 获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(this.responseText=="true"){
document.getElementById("username_err").style.display='';
}else {
//用户名不存在 ,清楚提示信息
document.getElementById("username_err").style.display='none';
}
//处理响应的结果
}
};
}
</script>
</body>
</html>
3.Axios异步框架
axious发送的ajax请求中的json都是json串 ,我们在java中需要将起转换成json对象 **JSON字符串转Java对象** User user = JSON.parseObject(jsonStr, User.class);
前端实现
- 引入 js 文件
<script src="js/axios-0.18.0.js"></script>
- 发送 ajax 请求
1.get 请求
axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
2.post 请求
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
2.请求方法别名
为了方便起见, Axios 已经为所有支持的请求方法提供了别名。如下:
get
请求 :axios.get(url[,config])
delete
请求 :axios.delete(url[,config])
head
请求 :axios.head(url[,config])
options
请求 :axios.option(url[,config])
post
请求:axios.post(url[,data[,config])
put
请求:axios.put(url[,data[,config])
patch
请求:axios.patch(url[,data[,config])
get方法:
axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan").then(function (resp) {
alert(resp.data);
});
post方法:
axios.post("http://localhost:8080/ajax-demo/axiosServlet","username=zhangsan").then(function (resp) {
alert(resp.data);
})
9.JSON
axious发送的ajax请求中的json都是json串 ,我们在java中需要将起转换成json对象
JSON字符串转Java对象
User user = JSON.parseObject(jsonStr, User.class);
1. JSON 基础语法
JSON
的格式
{
"name":"zhangsan",
"age":23,
"city":"北京"
}
JSON
本质就是一个字符串,但是该字符串内容是有一定的格式要求的。
var 变量名 = ‘{“key”:value,“key”:value,…}’; —这是JSON串
JSON
串的键要求必须使用双引号括起来,而值根据要表示的类型确定。value 的数据类型分为如下
- 数字(整数或浮点数)
- 字符串(使用双引号括起来)
- 逻辑值(true或者false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
var jsonStr = ‘{“name”:“zhangsan”,“age”:23,“addr”:[“北京”,“上海”,“西安”]}’
2.JSON串和Java对象的相互转换
基础方法
//json串
var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}' json串
var parse = JSON.parse(jsonStr); 转换成json对象
let s = JSON.stringify(parse); json对象转换成json串
Fastjson
是阿里巴巴提供的一个Java语言编写的高性能功能完善的 JSON
库,是目前Java语言中最快的 JSON
库,可以实现 Java
对象和 JSON
字符串的相互转换。
1.Fastjson 使用
- 1.导入坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
- 2.Java对象转JSON
String jsonStr = JSON.toJSONString(obj);
- 3.JSON字符串转Java对象
User user = JSON.parseObject(jsonStr, User.class);
代码演示:
User user=new User();
user.setId(1);
user.setUsername("wangwei");
user.setPassword("wangwei");
//对象转JSON
String s = JSON.toJSONString(user);
System.out.println(s);
//JSON转对象
User user1 = JSON.parseObject("{\"id\":1,\"password\":\"wangwei\",\"username\":\"wangwei\"}", User.class);
System.out.println(user1);
10.Servlet的优化
每一个功能都需要定义一个 servlet
,一个模块需要实现增删改查功能,就需要4个 servlet
,模块一多就会造成servlet
泛滥。此时我们就想 servlet
能不能像 service
一样,一个模块只定义一个 servlet
,而每一个功能只需要在该 servlet
中定义对应的方法。
@WebServlet("/brand/*")
public class BrandServlet {
//查询所有
public void selectAll(...) {}
//添加数据
public void add(...) {}
//修改数据
public void update(...) {}
//删除删除
public void delete(...) {}
}
servlet
,tomcat
会自动的调用 service()
方法,之前我们在自定义的 servlet
中重写 doGet()
方法和 doPost()
方法,当我们访问该 servlet
时会根据请求方式将请求分发给 doGet()
或者 doPost()
方法
如下图: