文章目录
- 一、Tomcat
- 1. BS 与 CS 开发介绍
- 1.1 BS 开发
- 1.2 CS 开发
- 2. 浏览器访问 web 服务过程详解(面试题)
- 2.1 回到前面的 JavaWeb 开发技术栈图
- 2.2 浏览器访问 web 服务器文件的 UML时序图(过程) !
- 二、动态 WEB 开发核心-Servlet
- 1. 为什么会出现 Servlet
- 2. 什么是 Servlet
- 3. Servlet 在 JavaWeb 项目位置
- 4. Servlet 基本使用
- 4.1 Servlet开发方式说明
- 4.2浏览器调用 Servlet 流程分析[重点]
- 4.3 Servlet 生命周期
- 6. ServletConfig
- 6.1 基本介绍
- 6.2 ServletConfig 能干什么
- 7. ServletContext
- 7.1 为什么需要 ServletContext
- 7.2 应用实例-简单的网站访问次数计数器
- 8. HttpServletRequest
- 8.1 HttpServletRequest介绍
- 8.2 HttpServletRequest 常用方法
- 8.3 应用实例
- 8.4 注意事项和细节
- 8.5 请求转发[重要]
- 8.5.1 为什么需要请求转发
- 8.5.2 请求转发声明
- 8.5.3 实现请求转发原理图
- 8.5.4 请求转发注意事项和细节
- 9. HttpServletResponse
- 9.1 HttpServletResponse介绍
- 9.2 HttpServletResponse 类图
- 9.3 向客户端返回数据方法
- 9.4 请求重定向[重要]
- 9.4.1 请求重定向原理示意图
- 9.4.2 请求重定向注意事项和细节
- 三、WEB开发通信协议-HTTP 协议
- 1. HTTP请求包分析(GET)
- 2. HTTP请求包分析(POST)
- 3. GET 请求和 POST请求分别有哪些?
- 4. HTTP 请求中怎么选择 GET 和 POST 方式
- 5. HTTP响应包分析
- 6. 状态码
- 6.1 状态码 301
- 6.2 状态码 304
- 7. MIME 类型
- 7.1 MIME 介绍
- 7.2 常见的 MIME 类型
- 8. HTTP 作业
- 四、WEB 工程路径专题
- 1. 解决方案: base 标签
- 1.1 base 基本介绍
- 1.2 应用实例
- 1.3 WEB工程路径注意事项和细节
- 五、Web 开发会话技术 -Cookie&Session
- 1. 会话
- 1.1 基本介绍
- 1.2 会话的两种技术
- 2. cookie 介绍
- 3. Cookie 生命周期
- 4 . Cookie 有效路径
- 5. Cookie注意事项和细节
- 6. Session 有什么用
- 7. Session 基本原理
- 7.1 Session 原理示意图
- 7.2 Session 可以做什么
- 7.3 如何理解 Session
- 8. session 常用方法
- 9. session 底层实现机制 *
- 9.1 原理分析图
- 9.2 代码演示
- 1.7.3 Session 实现原理动画
- 10. Session 生命周期
- 10.1 session 生命周期-说明
- 10.2 Session 生命周期-应用实例
- 11. Session 经典案例-防止非法进入管理页面
- 11.1 作业布置
- 11.2 作业代码
- 六、JavaWeb三大组件之监听器 Listener
- 1. Listener 监听器介绍
- 2. JavaWeb 的监听器
- 2.1 ServletContextListener 监听器
- 2.2 ServletContextAttributeListener 监听器
- 2.3 其他监听器-使用较少
- 2.3.1 HttpSessionListener 监听器
- 2.3.2 HttpSessionAttributeListener 监听器
- 2.3.3 **ServletRequestListener** **监听器**
- 2.3.4 **ServletRequestAttributeListener** **监听器**
- 2.3.5 HttpSessionBindingListener 感知监听器
- 2.3.6 **HttpSessionActivationListener** **感知监听器**
- 七、JavaWeb 三大组件之 过滤器 Filter
- 1. Filter 过滤器说明
- 2. Filter 过滤器基本原理
- 3. Filter 应用案例
- 4. Filter 过滤器 url-pattern
- 5. Filter 过滤器生命周期
- 6. FilterConfig
- 7. FilterChain 过滤器链
- 八、数据交换和异步请求 - JSON & Ajax
- 1. JSON 介绍
- 2. JSON 规则
- 3. JSON 在 java 中使用
- 1. 说明
- 2. JSON 在 java中 应用场景
- 3. 应用实例 JSON 在 java中应用场景
- 九、Ajax 基本介绍
- 1. Ajax 是什么
- 2. Ajax 经典应用场景
- 3. Ajax 原理示意图
- 4. JavaScript 原生 Ajax 请求
- 十、线程数据共享和安全 ThreadLocal
- 1. 什么是 ThreadLocal
- 2. 快速人门 ThreadLocal
- 3. ThreadLocal 源码解读+画图
一、Tomcat
1. BS 与 CS 开发介绍
1.1 BS 开发
- B: browser(浏览器, 种类太多 ff, chrome, ie, edge,)
- S: Server(服务端, 考虑很多)
- 示意图
对 BS 的解读
(1) 兼容性 , 因为浏览器的种类很多,发现你写的程序,在某个浏览器会出现问题,其它浏览器正常
(2) 安全性, 通常情况下,BS 安全性不如 CS 好控制
(3) 易用性, BS 好于 CS, 浏览器电脑有
(4) 扩展性, BS 相对统一,只需要写 Server
1.2 CS 开发
1. C: Client(客户端)
2. S: Server(服务端)
示意图
2. 浏览器访问 web 服务过程详解(面试题)
2.1 回到前面的 JavaWeb 开发技术栈图
2.2 浏览器访问 web 服务器文件的 UML时序图(过程) !
二、动态 WEB 开发核心-Servlet
1. 为什么会出现 Servlet
- 引入我们动态网页(能和用户交互)技术 --> Servlet
- 对 JavaWeb 技术体系的流程图改造说明(细化).[整体的概念]
2. 什么是 Servlet
- 什么是 Servlet
Servlet 在开发动态 WEB 工程中,得到广泛的应用,掌握好 Servlet 非常重要了, Servlet(基石)是 SpringMVC 的基础
- Servlet(Java 服务端小程序),它的特点
- 他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)
- 他是用java语言编写的, 本质就是Java类
- 他是按照Servlet规范开发的(除了tomcat->Servlet weblogic->Servlet)
- 功能强大,可以完成几乎所有的网站功能(在以前,我们老程员,使用Servlet开发网站) 技术栈要求高
3. Servlet 在 JavaWeb 项目位置
4. Servlet 基本使用
4.1 Servlet开发方式说明
- servlet3.0 前使用 web.xml , servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml配置
- 如何看 servlet 版本
4.2浏览器调用 Servlet 流程分析[重点]
4.3 Servlet 生命周期
- 主要有三个方法
- init() 初始化阶段
- service() 处理浏览器请求阶段
- destroy() 终止阶段
- 示意图(比较重要,而且形象)
- 初始化阶段(init 方法)
Servlet 容器(比如:Tomcat) 加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例并调用 init()方法,init()方法只调用一次。
Servlet 容器如果遇到上面的情况会重新装载 Servlet:
- Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加 1 1 表示装载的顺序
- 在 Servlet 容器启动后,浏览器首次向 Servlet 发送请求(这个前面说过)
- Servlet 重新装载时(比如 tomcat 进行 redeploy【redeploy 会销毁所有的 Servlet 实例】),浏览器再向 Servlet 发送请求时,相当于第 1 次请求,先实例化Servlet --》 再调用 init(…) 方法 --》 调用service(…)方法
- 处理浏览器请求阶段(service 方法)
- 每收到一个 http 请求,服务器就会产生一个新的线程取处理[线程]
- 创建一个用于封装 HTTP请求 消息的 ServletRequest 对象和一个代表 HTTP响应 消息的 ServletResponse 对象
- 然后调用 Servlet 的 service() 方法并将请求和响应对象作为参数传递进去
- 终止阶段 destroy方法(体现 Servlet 完整的生命周期)
当 web 应用被终止,或者 Servlet 容器终止运行,或者 Servlet 类重新装载时,会调用 destroy() 方法,比如重启 Tomcat、或者 redeploye web (重新发布)应用。
- 上个图,切记一定要自己练习一遍
工程路径:D:\IDEA_code\xjz_javaweb\servlet
6. ServletConfig
6.1 基本介绍
- ServletConfig 类是为 Servlet 程序的配置信息的类
- Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建
- Servlet 程序默认是第 1 次访问的时候创建,ServletConfig 在 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象
6.2 ServletConfig 能干什么
-
获取 Servlet 程序的 servlet-name 的值
-
获取初始化参数 init-param
-
获取 ServletContext 对象
应用实例…
示意图(思路分析)
7. ServletContext
7.1 为什么需要 ServletContext
-
先看一个需求: 如果我们希望统计某个 web 应用的所有 Servlet 被访问的次数,怎么办
-
方案 1-DB
- 方案 2-ServletContext
由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过 ServletContext 对象来实现多个 Servlet 间通讯。ServletContext 对象通常也被称之为域对象。【示意图】
详情看 韩顺平JavaWeb-笔记
7.2 应用实例-简单的网站访问次数计数器
- 需求:完成一个简单的网站访问次数计数器
- 使用 Chrome 访问 payServlet, 每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
- 使用火狐访问 OrderServlet,每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
代码如下D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\servletcontext
运行结果
8. HttpServletRequest
8.1 HttpServletRequest介绍
- HttpServletRequest 对象代表客户端的请求
- 当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象中
- 通过这个对象的方法,可以获得客户端这些信息。
我们是OOP程序员,第一步,看它的类图(继承关系和方法)
8.2 HttpServletRequest 常用方法
-
getRequestURI() 获取请求的资源路径 http://localhost:8080**/servlet/loginServlet**
-
getRequestURL() 获 取 请 求 的 统 一 资 源 定 位 符 ( 绝 对 路 径 )
http://localhost:8080/servlet/loginServlet -
getRemoteHost() 获取客户端的 主机, getRemoteAddr()
-
getHeader() 获取请求头
-
getParameter() 获取请求的参数
-
getParameterValues() 获取请求的参数(多个值的时候使用) , 比如 checkbox, 返回的数组
-
getMethod() 获取请求的方式 GET 或 POST
-
setAttribute(key, value); 设置域数据
-
getAttribute(key); 获取域数据
-
getRequestDispatcher() 获取请求转发对象, 请求转发的核心对象
8.3 应用实例
package com.xjz.servlet.request;
import com.sun.org.apache.regexp.internal.RE;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author xjz_2002
* @version 1.0
*/
public class HttpServletRequestMethods extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里我们使用 request 对象,获取表单提交的各种数据
System.out.println("HttpServletRequestMethods doPost() 被调用...");
/***********************************
* 获取和 http 请求头相关信息
***********************************/
System.out.println("请求的资源路径 URI= " + request.getRequestURI()); ///servlet/requestMethods
//http://主机/uri
System.out.println("请求的统一资源定位符(决定路径) URL= " + request.getRequestURL());// http://localhost:8080/servlet/requestMethods
System.out.println("请求的客户端 ip地址=" + request.getRemoteAddr());//本地就是 127.0.0.1
//思考题:如发现某个 ip 在 10s 中,访问的次数超过 100 次,就封 ip
//实现思路:1 用一个集合 concurrentHashmap[ip:访问次数] 2[线程/定时扫描]3 做成处理
System.out.println("http 请求头 HOST=" + request.getHeader("Host")); //HOST=localhost:8080
//说明:如果我们希望得到请求头的相关信息,可以使用request.getHeader(""请求头字段)
System.out.println("该请求的发起地址是=" + request.getHeader("Referer"));//
//请获取访问网站的浏览器是什么?
String userAgent = request.getHeader("User-agent");
System.out.println("userAgent=" + userAgent);
String[] s = userAgent.split(" ");
System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
// 主要是 Get / Post
System.out.println("http 请求方式= " + request.getMethod());
/***********************************
* 获取和请求参数相关信息, 注意要求在返回数据前,获取参数
***********************************/
//1. 获取表单的数据[单个数据]
//username=tom&pwd=123&hobby=lyl&hobby=lzq
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
//2. 获取表单的一组数据
String[] hobbies = request.getParameterValues("hobby");
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
//增强 for 循环的快捷键 iter->回车即可 , 能使用快捷键,就使用快捷键
for (String hobby : hobbies) {
System.out.println("hobby= " + hobby);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//合并
doPost(request, response);
}
}
8.4 注意事项和细节
-
获 取 doPost 参 数 中 文 乱 码 解 决 方 案 , 注 意 setCharacterEncoding(“utf-8”) 要 写 在request.getParameter()前
-
注意:如果通过 PrintWriter writer, 有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)
-
处理 http 响应数据中文乱码问题
-
再次理解 Http 协议响应 Content-Type 的含义, 比如 text/plain application/x-tar**
8.5 请求转发[重要]
8.5.1 为什么需要请求转发
- 目前我们学习的都是一次请求,对应一个 Servlet,如图
- 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成一个任务(Servlet 链, 流水作业) 如图:
8.5.2 请求转发声明
- 实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理
- HttpServletRequest 对象(也叫 Request 对象)提供了一个 getRequestDispatcher 方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的 forward 方法可以实现请求转发
- request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据通过 request 对象带给其它 web 资源处理
- setAttribute方法
- getAttribute方法
- removeAttribute方法
- getAttributeNames方法
8.5.3 实现请求转发原理图
请求转发原理示意图
8.5.4 请求转发注意事项和细节
- 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
- 在同一次 HTTP 请求中,进行多次转发,仍然是一次 HTTP 请求
- 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
- 可以转发到 WEB-INF 目录下(后面做项目使用)
- 不能访问当前 WEB 工程外的资源
- 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付[演示]
9. HttpServletResponse
9.1 HttpServletResponse介绍
-
每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用。
-
HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse 对象来进行设置即可
9.2 HttpServletResponse 类图
9.3 向客户端返回数据方法
- 字节流 getOutputStream(); 常用于下载(处理二进制数据)
- 字符流 getWriter(); 常用于回传字符串
- (细节:)两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错
9.4 请求重定向[重要]
9.4.1 请求重定向原理示意图
-
请求重定向指:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web资源,这称之为请求重定向
-
请求重定向原理示意图
相关代码路径:D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\response\DownServlet.java
D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\response\DownServletNew.java
9.4.2 请求重定向注意事项和细节
- 最佳应用场景:网站迁移,比如原域名是 www.baidu.com 迁移到 www.baidu.cn ,但是百度抓取的还是原来网址.
- 浏览器地址会发生变化,本质是两次 http 请求.
- 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
- 不能重定向到 /WEB-INF 下的资源
- 可以重定向到 Web 工程以外的资源, 比如 到 www.baidu.com 【在前面的案例演示】
- 重定向有两种方式, 推荐使用第 1 种.
response.sendRedirect("/servlet/downServletNew");
//第二种重定向的写法
response.setStatus(302);//设置 http响应的状态码
//设置http响应的 Location: /servlet/downServletNew
response.setHeader("Location","/servlet/downServletNew");
- 动态获取到 application context
//5. 动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath=" + contextPath);//servlet
//response.sendRedirect("/servlet/downServletNew");
response.sendRedirect(contextPath + "/downServletNew");
三、WEB开发通信协议-HTTP 协议
1. HTTP请求包分析(GET)
2. HTTP请求包分析(POST)
3. GET 请求和 POST请求分别有哪些?
4. HTTP 请求中怎么选择 GET 和 POST 方式
5. HTTP响应包分析
-
HTTP响应包括3个部分
- 响应行
- 响应头
- 响应体
-
HTTP响应包分析图
6. 状态码
6.1 状态码 301
6.2 状态码 304
7. MIME 类型
7.1 MIME 介绍
7.2 常见的 MIME 类型
8. HTTP 作业
四、WEB 工程路径专题
- 先看一个问题 => 怎么解决访问资源的问题
- 工程路径解决方案
- 说明: 使用相对路径来解决, 一个非常重要的规则:页面所有的相对路径,在默认情况下,都会参考当前浏览器地址栏的路径 http://ip:port/工程名/ + 资源来进行跳转。所以我们可以直接这样写
- 相对路径带来的问题举例 => 示意图
- 如果需要指定页面相对路径参考的的路径,可以使用 base 标签来指定
1. 解决方案: base 标签
1.1 base 基本介绍
- base 标签是 HTML 语言中的基准网址标记,它是一个单标签,位于网页头部文件的 head标签内
- 一个页面最多只能使用一个 base 元素,用来提供一个指定的默认目标,是一种表达路径和连接网址的标记。
- 常见的 url 路径形式分别有相对路径与绝对路径,如果 base 标签指定了目标,浏览器将通过这个目标来解析当前文档中的所有相对路径,包括的标签有(a、img、link、form)
- 也就是说,浏览器解析时会在路径前加上 base 给的目标,而页面中的相对路径也都转换成了绝对路径。使用了 base 标签就应带上 href 属性和 target 属性
1.2 应用实例
D:\IDEA_code\xjz_javaweb\webpath\src\com\xjz\servlet
1.3 WEB工程路径注意事项和细节
- Web 工程的相对路径和绝对路径
- 在实际开发中,路径都使用绝对路径,而不是相对路径
- 在 web 中 / 斜杠 如果被浏览器解析,得到的地址是:http://ip[域名]:port/ 比如: <ahref=“/”>斜杠
- 在 web 中 / 斜杠 如果被服务器解析,得到的地址是:http://ip[域名]:port/工程路径/,你也可以理解成 /工程路径/ 下面的几种情况就是如此:
- 在 javaWeb 中 路径最后带 / 和 不带 / 含义不同, 一定要小心,
比如 网址 : servlet03 表示资源
网址 : servlet03 表示路径
- 特别说明:重定向 response.sendRediect(“/”); 这条语句虽然是在服务器执行的,但是,服务器是把斜杠 / 发送给浏览器解析。因此得到地址 http://ip[域名]:port/
小结: 在编写资源路径时: , 考虑这么几点
(1) 这个路径 前面有没有 /
(2) 这个路径 在哪里被解析 [服务器还是浏览器] , 如果前面有 / , 并且是在 浏览器被解析的 被解析成 http://ip:port/ , 如果在服务器端被解析 , 被解析成 /工程路径/
(3) 如果这个路径,前面没有 / , 并且在浏览器被解析,则以浏览器当前的地址栏 去掉资源部分,作为一个相对路径.
(4) 这个路径,最后有没有 / , 如果最后有/ 表示路径, 如果没有 / 表示资源
五、Web 开发会话技术 -Cookie&Session
1. 会话
1.1 基本介绍
-
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个 web 资源,然后关闭浏览器,整个过程称之为一个会话。
-
会话过程中要解决的一些问题?
- 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据
- 例如:多个用户点击超链接通过一个 servlet 各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,以便于这些用户点结帐 servlet 时,结帐servlet 可以得到用户各自购买的商品为用户结帐。
1.2 会话的两种技术
Cookie(小甜饼)是客户端技术,服务器把每个用户的数据以 cookie 的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的 web 资源时,就会带着各自的数据去。这样,web 资源处理的就是用户各自的数据了。【简单示意图】
2. cookie 介绍
- Cookie 是服务器在客户端保存用户的信息,比如登录名,浏览历史等, 就可以以 cookie方式保存.
- Cookie 信息就像是小甜饼(cookie 中文)一样,数据量并不大,服务器端在需要的时候可以从客户端/浏览器读取(http 协议),可以通过图来理解
演示 Cookie 底层实现机制**,** 创建和读取 Cookie
D:\IDEA_code\xjz_javaweb\cookie-session\src\com\xjz\cookie\CreateCookie.java
3. Cookie 生命周期
-
Cookie 的生命周期指的是如何管理 Cookie 什么时候被销毁(删除)
-
setMaxAge()
● 正数,表示在指定的秒数后过期
● 负数,表示浏览器关闭,Cookie 就会被删除(默认值是-1)
● 0,表示马上删除 Cookie
4 . Cookie 有效路径
-
Cookie 有效路径 Path 的设置
-
Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。 path属性是通过请求的地址来进行有效的过滤
-
规则如下
5. Cookie注意事项和细节
- 一个 Cookie 只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
- 一个 WEB 站点可以给一个浏览器发送多个 Cookie,一个浏览器也可以存储多个 WEB 站点提供的 Cookie。
- cookie 的总数量没有限制,但是每个域名的 COOKIE 数量和每个 COOKIE 的大小是有限制的 (不同的浏览器限制不同, 知道即可) , Cookie 不适合存放数据量大的信息。
- 注意,删除 cookie 时,path 必须一致,否则不会删除
- Java servlet 中 cookie 中文乱码解决 [代码演示 EncoderCookie.java ReadCookie2.java]
说明:如果存放中文的 cookie, 默认报错, 可以通过 URL 编码和解码来解决, 不建议存放中文的 cookie 信息
package com.xjz.cookie;
import javax.servlet.ServletException;
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.io.PrintWriter;
import java.net.URLEncoder;
/**
* @author xjz_2002
* @version 1.0
*/
public class EncoderCookie extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("EncoderCookie 被调用...");
//1. 创建 Cookie,有中文
//代码解读
//1) 如果直接存放中文的 cookie,报错 Control character in cookie value or attribute.
//2) 解决办法,就是将中文 编码成 URL编码 英文: Encode=编码
//3) 编码后,再保存即可
String name = URLEncoder.encode("程序员老徐", "utf-8");
Cookie cookie = new Cookie("name", name);
//2. 保存到浏览器
response.addCookie(cookie);
//3. 给浏览器返回信息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>设置中文cookie成功~</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
解码
package com.xjz.cookie;
import javax.servlet.ServletException;
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.io.PrintWriter;
import java.net.URLDecoder;
/**
* @author xjz_2002
* @version 1.0
*/
public class ServletReadCookie2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
Cookie[] cookies = request.getCookies();
//找到带有中文的 cookie
Cookie name = CookieUtils.readCookieByName("name", cookies);
//处理的中文乱码问题
writer.println("Cookie[" + name.getName()
+ "=" +
URLDecoder.decode(name.getValue(), "utf-8") + "] <br/>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
6. Session 有什么用
- 不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字,还可以随时去查看自己的购物车中的商品, 是如何实现的?
- 也就是说,一个用户在浏览网站不同页面时,服务器是如何知道是张三在浏览这个页面,还是李四在浏览这个页面?
● 解决之道—session 技术, 简单说
- Session 是服务器端技术,服务器在运行时为每一个用户的浏览器创建一个其独享的session 对象/集合
- 由于 session 为各个用户浏览器独享,所以用户在访问服务器的不同页面时,可以从各自的 session 中读取/添加数据, 从而完成相应任务
7. Session 基本原理
7.1 Session 原理示意图
- 当用户打开浏览器,访问某个网站, 操作 session 时,服务器就会在内存(在服务端)为该浏览器分配一个 session 对象,该 session 对象被这个浏览器独占, 如图
- 这个 session 对象也可看做是一个容器/集合,session 对象默认存在时间为 30min(这是在tomcat/conf/web.xml),也可修改
7.2 Session 可以做什么
- 网上商城中的购物车
- 保存登录用户的信息
- 将数据放入到 Session 中,供用户在访问不同页面时,实现跨页面访问数据
- 防止用户非法登录到某个页面
7.3 如何理解 Session
- session 存储结构示意图
- 你可以把 session 看作是一容器类似 HashMap,有两列(K-V),每一行就是 session 的一个属性。
- 每个属性包含有两个部分,一个是该属性的名字(String),另外一个是它的值(Object)
8. session 常用方法
-
创建和获取 Session,API 一样HttpSession hs=request.getSession();第 1 次调用是创建 Session 会话, 之后调用是获取创建好的 Session 对象
-
向 session 添加属性
hs.setAttribute(String name,Object val); -
从 session 得到某个属性
Object obj=hs.getAttribute(String name); -
从 session 删除调某个属性:
hs.removeAttribute(String name);
-
isNew(); 判断是不是刚创建出来的 Session
-
每个 Session 都有 1 个唯一标识 Id 值。通过 getId() 得到 Session 的会话 id 值
9. session 底层实现机制 *
9.1 原理分析图
- session 底层实现机制图解(重要)
9.2 代码演示
创建Session
package com.xjz.session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
*/
public class CreateSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("CreateSession 被调用..");
//1. 获取session,同时也可以创建session
HttpSession session = request.getSession();
//2. 给session 获取id
System.out.println("当前sessionId=" + session.getId());
//3. 给session 存放数据
session.setAttribute("key1", "value1");
//4. 给浏览器发送一个回复
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>创建/操作 session成功~");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 运行结果
第二次刷新后
- 读session
package com.xjz.session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
*/
public class ReadSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("ReadSession 被调用..");
//演示读取 session
//1. 获取session,如果没有session,也会创建
HttpSession session = request.getSession();
//2. 读取属性
Object key1 = session.getAttribute("key1");
if (key1 != null){
System.out.println("session属性 key1=" + (String) key1);
} else {
System.out.println("session中没有 key1属性 ");
}
//给浏览器回复一下
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>读取session成功~</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
我们可以看出,两个servlet的JSEESIONID是同一JSESSIONID
有了代码支撑,我们在回头看 Session 的原理图,就有更深刻的理解
1.7.3 Session 实现原理动画
- 服务器是如何实现一个 session 为一个用户浏览器服务的
10. Session 生命周期
10.1 session 生命周期-说明
- public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session 就会被销毁。
- 值为正数的时候,设定 Session 的超时时长。
- 负数表示永不超时
- public int getMaxInactiveInterval()获取 Session 的超时时间
- public void invalidate() 让当前 Session 会话立即无效
- 如果没有调用 setMaxInactiveInterval() 来指定 Session 的生命时长,Tomcat 会以 Session默认时长为准,Session 默认的超时为 30 分钟, 可以在 tomcat 的 web.xml 设置
- Session 的生命周期指的是 :客户端/浏览器两次请求最大间隔时长,而不是累积时长。即当客户端访问了自己的 session,session 的生命周期将从 0 开始重新计算。(代码解读: 指的是同一个会话两次请求之间的间隔时间)
- 底层: Tomcat 用一个线程来轮询会话状态,如果某个会话的空闲时间超过设定的最大值,则将该会话销毁
10.2 Session 生命周期-应用实例
- 需求:代码演示说明 Session 的生命周期
- 创建
package com.xjz.session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
*/
public class CreateSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("CreateSession2 被调用..");
//1. 创建 session
HttpSession session = request.getSession();
System.out.println("CreateSession2 sid= " + session.getId());
//2. 设置生命周期为 60s
session.setMaxInactiveInterval(60);
session.setAttribute("u","jack");
//3. 回复一下浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>创建session成功,设置生命周期 60s</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 读 Session
package com.xjz.session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author xjz_2002
* @version 1.0
*/
public class ReadSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ReadSession2 被调用..");
//1.获取到session
HttpSession session = request.getSession();
System.out.println("ReadSession2 sid= " + session.getId());
//2. 读取session的属性
Object u = session.getAttribute("u");
if (u != null){
System.out.println("获取到session属性 u=" + (String) u);
} else {
System.out.println("读取不到session属性 u 说明原来的session被销毁");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 删除session 使用 invalidate() 方法
package com.xjz.session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
*/
public class DeleteSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DeleteSession 被调用..");
//1. 演示如何删除session
HttpSession session = request.getSession();
session.invalidate();
//2. 如果我们要删除的是 session的某个属性
//session.removeAttribute("xxx");
//3. 回复一下浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>删除session成功</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
总结:Session 的生命周期
- 指的是两次访问 session 的最大间隔时间
- 如果你在 session 没有过期的情况下(默认30分钟),操作 session, 则会重新开始计算生命周期
- session 是否过期,是由服务器来维护和管理
- 如我们调用了 invaliate() 会直接将该 session 删除/销毁
- 如果希望删除 session 对象的某个属性, 使用 removeAttribute(“xx”)
11. Session 经典案例-防止非法进入管理页面
11.1 作业布置
- 需求说明:完成防止用户登录管理页面应用案例(如图)
说明:
- 只要密码为 666666, 我们认为就是登录成功
- 用户名不限制
- 如果验证成功,则进入管理页面 ManageServelt.java ,否则进入 error.html
- 如果用户直接访问 ManageServet.java , 重定向到到 login.html
11.2 作业代码
D:\IDEA_code\xjz_javaweb\cookie-session\src\com\xjz\session\homework
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/cs/loginCheckServlet" method="post">
用户名:<input type="text" name="username"><br/>
密 码:<input type="password" name="pwd"><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录失败</title>
<base href="/cs/">
</head>
<body>
<h1>登录失败</h1>
<a href="login.html">点击返回重新登录</a>
<!-- 如果没有bese标签,可以用如下返回,带/ 为绝对路径,
我们一般使用绝对路径,不带 / 为相对路径,不安全 !-->
<!--<a href="/login.html">点击返回重新登录</a>-->
</body>
</html>
LoginCheckServlet.java
package com.xjz.session.homework;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
*/
public class LoginCheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("LoginCheckServlet 被调用..");
//1. 获取提交表单的用户名和密码
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
// //2. 将用户名 存放到 cookie中,并响应给浏览器 ==> Cookie方法
// Cookie cookie = new Cookie("username",username);
// response.addCookie(cookie);
//3. 只要密码为 66666,我们认为就是登录成功,用户名不限制
if (pwd != null && pwd.length() != 0) {
if ("666666".equals(pwd)) {
//2. 将用户名 存放到 session 中,并响应给浏览器 ==> Session方法 (生命周期默认30min)
//把用户名保存到 session
HttpSession session = request.getSession();
session.setAttribute("loginuser",username);
//超时1s 即销毁,否则在超时时间内,直接访问manageServlet2 可直接查看 管理员
session.setMaxInactiveInterval(1);
System.out.println("验证成功~");
//请求转发
//注意:请求转发 是在服务端请求的,默认路径是 http://localhost:8080/工程路径
// 所以不需要带 /工程路径, 如果带了,访问地址为http://localhost:8080/cs/cs/login.html
// 请求转发 不能访问 web以外的资源,如果是访问 servlet,则需要使用重定向!!
// request.getRequestDispatcher("/manageServlet.html").forward(request, response);
// session 方法
request.getRequestDispatcher("/manageServlet2").forward(request, response);
} else {
//验证失败,密码不是666666,请求转发到 error.html
System.out.println("验证失败");
request.getRequestDispatcher("/error.html").forward(request, response);
}
} else {
// 回复给浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>密码不能为 null,请重新输入</h1>");
writer.flush();
writer.close();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
**ManageServlet.java **
使用 Cookie方法,用户直接访问 ManageServlet.java , 重定向到到 login.html
package com.xjz.session.homework;
import com.xjz.cookie.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
* 使用 Cookie方法,用户直接访问 ManageServlet.java , 重定向到到 login.html
*/
public class ManageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("ManageServlet 被调用..");
//获取referer,即从哪个网址请求过来的 url
String referer = request.getHeader("Referer");
System.out.println("referer=" + referer);
//判断打过来的请求 url是否是 login.html,如果不是,重定向到login
if ("http://localhost:8080/cs/login.html".equals(referer)) {
//获取提交表单的 username
Cookie[] cookies = request.getCookies();
//读取key为 username 的 cookie
Cookie usernameCookie = CookieUtils.readCookieByName("username", cookies);
//获取该 cookie 的 value值
String usernameVal = usernameCookie.getValue();
System.out.println("usernameCookie= " + usernameVal);//xjz_2002
// 给浏览器返回消息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>恭喜你,管理员:" + usernameVal + "</h1>");
writer.flush();
writer.close();
} else {
//如果用户直接访问 /manageServlet,重定向到 login.html
//注意:请求重定向是 response响应到服务器的, 所以路径前必须加 /工程路径
//String contextPath = request.getContextPath();// 工程路径 => /cs
response.sendRedirect(request.getContextPath() + "/login.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ManageServlet2.java
将用户名 存放到 cookie中,并响应给浏览器 ==> Session方法 (生命周期默认30min)
package com.xjz.session.homework;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author xjz_2002
* @version 1.0
* 将用户名 存放到 session中,并响应给浏览器 ==> Session方法 (生命周期默认30min)
*/
public class ManageServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断该用户是否登录过
HttpSession session = request.getSession();
Object userlogin = session.getAttribute("loginuser");
System.out.println("userlogin= " + userlogin);
if (userlogin == null) { //说明该用户没有登录
//重新登录 -> 请求重定向
// response.sendRedirect("/cs/login.html");
response.sendRedirect(request.getContextPath() + "/login.html");
return;
}
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>恭喜你,管理员:" + userlogin.toString() + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
六、JavaWeb三大组件之监听器 Listener
1. Listener 监听器介绍
-
Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
-
Listener 是 JavaEE 的规范,就是接口
-
监听器的作用是,监听某种变化(一般就是对象创建/销毁, 属性变化), 触发对应方法完成相应的任务
-
JavaWeb 中的监听器(共八个), 目前最常用的是ServletContextListener
2. JavaWeb 的监听器
2.1 ServletContextListener 监听器
-
作用:监听 ServletContext 创建或销毁(当我们 Web 应用启动时,就会创建 ServletContext),即生命周期监听,应用场景(1)加载初始化的配置文件;比如 spring 的配置文件 (2)任务调度(配合定时器 Timer/TimerTask)
-
相关方法
- 配置 web.xml
2.2 ServletContextAttributeListener 监听器
-
作用:监听 ServletContext 属性变化
-
相关方法
2.3 其他监听器-使用较少
2.3.1 HttpSessionListener 监听器
1. 作用:监听 Session 创建或销毁,即生命周期监听
2. 相关方法
- 使用方法和前面一样, 可以用于监控用户上线,离线
2.3.2 HttpSessionAttributeListener 监听器
- 作用:监听 Session 属性的变化
- 相关方法
- 使用少 , 使用方法和前面一样。
2.3.3 ServletRequestListener 监听器
1. ServletRequestListener 监听器
2. 作用:监听 Request 创建或销毁,即 Request 生命周期监听
相关方法
- 可以用来监控, 某个 IP 访问我们网站的频率, 日志记录 ,访问资源的情况.
2.3.4 ServletRequestAttributeListener 监听器
1. 作用:监听 Request 属性变化
2. 相关方法
- 使用方法和前面类似
2.3.5 HttpSessionBindingListener 感知监听器
2.3.6 HttpSessionActivationListener 感知监听器
七、JavaWeb 三大组件之 过滤器 Filter
1. Filter 过滤器说明
- 过滤器介绍
**1. Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
- Filter 过滤器是 JavaEE 的规范,是接口
- Filter 过滤器它的作用是:拦截请求,过滤响应。
- 应用场景
- 权限检查
- 日记操作
- 事务管理
2. Filter 过滤器基本原理
3. Filter 应用案例
D:\IDEA_code\xjz_javaweb\filter
4. Filter 过滤器 url-pattern
1、url-pattern : Filter 的拦截路径, 即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
2.、精确匹配 /a.jsp 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截
3、目录匹配 /manage/*对应的 请求地址http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截
4、后缀名匹配 *.jsp 后缀名可变,比如 *.action *.do 等等对应的 请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截
5、Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在
5. Filter 过滤器生命周期
- Filter 过滤器生命周期 图解
6. FilterConfig
- FilterConfig 接口图
- FilterConfig 说明
- FilterConfig 是 Filter 过滤器的配置类
- Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
- FilterConfig 对象作用是获取 filter 过滤器的配置内容
- 应用实例
D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\XjzFilterConfig.java
7. FilterChain 过滤器链
- FilterChain 简介
一句话: FilterChain: 在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链。
- 基本原理试示意图
- 应用实例
D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\AFilter.java/BFilter.java
- 运行结果
- FilterChain 注意事项和细节
-
多个 filter 和目标资源在一次 http 请求,在同一个线程中
-
当一个请求 url 和 filter 的 url-pattern 匹配时, 才会被执行, 如果有多个匹配上,就会顺序执行,形成一个 filter 调用链(底层可以使用一个数据结构搞定)
-
多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象
-
多个 filter 执行顺序,和 web.xml 配置顺序保持一致.
-
chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法, 如果后面没有过滤器,则执行目标资源。
-
小结:注意执行过滤器链时, 顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter() -> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 -> 返回给浏览器页面/数据
八、数据交换和异步请求 - JSON & Ajax
1. JSON 介绍
-
JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
-
JSON 是轻量级的文本数据交换格式
-
JSON 独立于语言 [老韩解读:即 java 、php、asp.net , go 等都可以使用 JSON]
-
JSON 具有自我描述性,更易理解, 一句话,非常的好用…
2. JSON 规则
- 映射(元素/属性)用冒号 : 表示,“名称”:值 , 注意名称是字符串,因此要用双引号引起来
- 并列的数据之间用逗号 , 分隔。“名称 1”:值,“名称 2”:值
- 映射的集合(对象)用大括号 {} 表示。{“名称 1”:值,“名称 2”:值}
- 并列数据的集合(数组)用方括号 [] 表示。 [{“名称 1”:值,“名称 2”:值}, {“名称 1”:值," 名称 2":值}]
- 元素值类型:string, number, object, array, true, false, null
3. JSON 在 java 中使用
1. 说明
-
java 中使用 json,需要引入到第 3 方的包 gson.jar
-
Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。
-
可以对 JSON 字符串 和 Java 对象相互转换
2. JSON 在 java中 应用场景
- Javabean 对象和 json 字符串 的转换
- List 对象和 json 字符串 的转换
- map 对象和 json 字符串 的转换
- 应用场景示意图
3. 应用实例 JSON 在 java中应用场景
JavaJson.java
package com.xjz.json;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xjz_2002
* @version 1.0
*/
public class JavaJson {
//代码解读
//1. 要把复杂的 json 字符串转换成为 java 对象。需要继承 TypeToken 类
//2. TypeToken 是一个自定义泛型类,在创建时,需要指定具体类型,这里我们指定为 List<Book>
// ,如果同学们忘记了,回去看 java 基础的泛型知识点
//3. TypeToken 是由 gson 包提供的
static class BookType extends TypeToken<List<Book>> {
}
public static void main(String[] args) {
// new 一个 gson 对象。引入 gson 包
Gson gson = new Gson();
// java 对象和 json 的转换
System.out.println("\n==== 1. java 对象 和 json 的转换 ====");
Book book = new Book(100, "我爱学Java");
//1. 演示把 javabean -> json字符串
String strBook = gson.toJson(book);
System.out.println("strBook= " + strBook);
//2. json字符串 -> javabean
//代码解读
//(1) strBook 就是 json字符串
//(2) Book.class 指定将 json字符串转成 Book对象
//(3) 底层是反射机制
Book book2 = gson.fromJson(strBook, Book.class);
System.out.println("book2= " + book2);
//2. List 集合 和 json 的转换
System.out.println("\n==== 2. List 集合 和 json 的转换 ====");
List<Book> bookList = new ArrayList<>();
bookList.add(new Book(200, "男人帮"));
bookList.add(new Book(300, "女人帮"));
// 将 list 转成 json对象
String bookListStr = gson.toJson(bookList);
System.out.println("bookListStr= " + bookListStr);
//将 json 字符串 转成 List 集合方式 1
Type type = new BookType().getType();
System.out.println("type= " + type); // java.util.List<com.xjz.json.Book>
List<Book> bookList2 = gson.fromJson(bookListStr, type);
System.out.println("bookList2= " + bookList2);
//将 json 字符串 转成 List 集合方式 2
//代码解读
//(1) 如果我们 new TypeToken<List<Book>>() 提示
// 'TypeToken()' has protected access in 'com.google.gson.reflect.TypeToken'
//(2) 因为 TypeToken 的无参构造器是 protected , 而 new TypeToken<List<Book>>() 就是调用其无参构造器
//(3) 根据java基础,如果一个方法是 protected,而且不在同一个包中,是不能直接访问的,因此报错
//(4) 为什么 new TypeToken<List<Book>>(){} 使用就可以,这里就涉及到 匿名内部类的知识点.
//(5) 当 new TypeToken<List<Book>>(){} 其实这个类型不是 TypeToken,而是一个 匿名内部类(可以理解为TypeToken的子类)
//(6) 而且这个匿名内部类是有自己的无参构造器(隐式),根据java基础规则:当执行子类的无参构造器时,默认super()
//Type type = new TypeToken<List<Book>>(){}.getType(); // java.util.List<com.xjz.json.Book>
List<Book> bookList3 = gson.fromJson(bookListStr, new TypeToken<List<Book>>() {}.getType());
System.out.println("bookList3= " + bookList3);
//3. map 集合 -> json 字符串
Map<String, Book> bookMap = new HashMap<>();
bookMap.put("k1",new Book(10,"三国演义"));
bookMap.put("k2",new Book(20,"水浒传"));
// 把 map 集合 -> json 字符串
String strBookMap = gson.toJson(bookMap);
System.out.println("strBookMap= " + strBookMap + "类型 = "+strBookMap.getClass());
// 把 json 字符串 -> map 集合
// new TypeToken<Map<String, Book>>() {}.getType() => java.util.Map<com.xjz.json.Book>
Map<String, Book> bookMap2 = gson.fromJson(strBookMap,
new TypeToken<Map<String, Book>>() {
}.getType());
System.out.println("bookMap2= " + bookMap2);
}
}
九、Ajax 基本介绍
1. Ajax 是什么
- AJAX 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML)
- Ajax 是一种浏览器异步发起请求(指定发哪些数据),局部更新页面的技术
2. Ajax 经典应用场景
- 搜索引擎根据用户输入关键字,自动提示检索关键字
- 动态加载数据,按需取得数据【树形菜单、联动菜单…】
- 改善用户体验。【输入内容前提示、带进度条文件上传…】
- 电子商务应用。 【购物车、邮件订阅…】
- 访问第三方服务。【访问搜索服务、rss 阅读器】
- 页面局部刷新, https://piaofang.maoyan.com/dashboard
3. Ajax 原理示意图
- 传统的WEB应用
- Ajax 原理示意图
4. JavaScript 原生 Ajax 请求
在线文档:https://www.w3school.com.cn/js/js_ajax_intro.asp
- 应用实例-验证用户名是否存在
演示 javascript 发送原生 ajax 请求的案例
-
在输入框输入用户名
-
点击验证用户名, 使用 ajax 方式, 服务端验证该用户名是否已经占用了, 如果该用户
已经占用, 以 json 格式返回该用户信息 -
假定用户名为 king , 就不可用, 其它用户名可以=》 后面我们接入 DB[Mysql+JDBC]
-
对页面进行局部刷新, 显示返回信息
-
小思考: 为什么直接返回用户名是否可用信息, 完成案例后, 再思考?
十、线程数据共享和安全 ThreadLocal
1. 什么是 ThreadLocal
- ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
- ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]
- ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法
- 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例
- 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
- ThreadLocal 中保存数据,在线程销毁后,会自动释放
2. 快速人门 ThreadLocal
ThreadLocalTest.java
package com.xjz.threadlocal;
/**
* @author xjz_2002
* @version 1.0
*/
public class ThreadLocalTest {
public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
public static class Task implements Runnable{
@Override
public void run() {
Dog dog = new Dog();
Pig pig = new Pig();
/**
* ===== set源码分析 只要明白这个机制,后面的 set get 全部通透=======
*
* public void set(T value) {
* //获取当前线程
* Thread t = Thread.currentThread();
* //获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals,
* // ,类型是 ThreadLocal 的静态内部类
* //threadLocals 有一个属性 Entry[],类型 }ThreadLocal.ThreadLocalMap.Entry
* // k-> ThreadLocal 对象 v -> 值
* ThreadLocalMap map = getMap(t);
* if (map != null)
* map.set(this, value); //存放这里的 this 就是 ThreadLocal1,可以 debug源码,一目了然
* else
* createMap(t, value); //创建
* }
*
* ======= getMap 方法源码======
* ThreadLocalMap getMap(Thread t) { //获取当前线程的 ThreadLocal.ThreadLocalMap
* return t.threadLocals;
* }
*
* 说明:
* 1. ThreadLocalMap 对象对象是和当前 Thread对象的绑定属性
* 2. ThreadLocalMap 对象含有 Entry[] table; 这个 Entry(K,V)
* 3. 这个 key 就是 ThreadLocal 对象, V 就是你要在放入的对象,比如 dog
* 4. 当执行了 了 threadLocal.set(dog) 后,内存布局图为 wps[看图]
*
*/
threadLocal1.set(dog);
threadLocal2.set(pig);// 会替换 dog
//如果希望在同一个线程共享多个对象/数据,就只能再创建一个 ThreadLocal 对象
//threadLocal2.set(pig);
System.out.println("在 run方法中 线程 name=" + Thread.currentThread().getName()
+ " 放入 ThreadLocal 的数据= " + dog );
new T1Service().update();
}
}
public static void main(String[] args) {
for (int i = 0; i < 1; i++) {
new Thread(new Task()).start();//启动一个新的线程,注意不是主线程
}
System.out.println("在 main 方法中 ThreadLocal 的数据=" + threadLocal1.get());
}
}
T1Service.java
package com.xjz.threadlocal;
/**
* @author xjz_2002
* @version 1.0
*/
public class T1Service {
public void update(){
String name = Thread.currentThread().getName();
/**
* ====== ThreadLocalTest.threadLocal.get() 源码====
* public T get() {
* Thread t = Thread.currentThread();
* ThreadLocalMap map = getMap(t);
* if (map != null) {
* ThreadLocalMap.Entry e = map.getEntry(this);
* if (e != null) {
* @SuppressWarnings("unchecked")
* T result = (T)e.value;
* return result;
* }
* }
* return setInitialValue();
* }
*
* 代码解读
* 1. 先得到当前线程对象
* 2. 获取当前对象绑定的 ThreadLocalMap 对象
* 3. 根据 threadLocal对象(key)获取 ThreadLocalMap 对象的 Entry数组的 Entry对象 [hash]
* 4. 取出 Entry对象的 value 就是放入到 ThreadLocal对象的数据 dog
*
*/
System.out.println("在 T1Service 的 update() 线程 name=" +
name + "threadLocal 的数据= " + ThreadLocalTest.threadLocal1.get());
System.out.println("在 T1Service 的 update() 线程 name=" +
name + "threadLocal 的数据= " + ThreadLocalTest.threadLocal2.get());
new T2DAO().update();
}
}
T2DAO.java
package com.xjz.threadlocal;
/**
* @author xjz_2002
* @version 1.0
*/
public class T2DAO {
public void update(){
String name = Thread.currentThread().getName();
System.out.println("在 T2DAO 的 update() 线程是=" + name
+ "threadlocal 数据是=" + ThreadLocalTest.threadLocal1.get());
System.out.println("在 T2DAO 的 update() 线程是=" + name
+ "threadlocal 数据是=" + ThreadLocalTest.threadLocal2.get());
}
}
6. 完成测试, 检测 ThreadLocal 可以实现在同一个线程数据安全共享
3. ThreadLocal 源码解读+画图
- ThreadLocal 原理分析图(重点 set 和 get)
- Debug 源码图,非常重要