对 BS 的解读
(1) 兼容性 , 因为浏览器的种类很多,发现你写的程序,在某个浏览器会出现问题,其它浏览器正常
(2) 安全性, 通常情况下,BS 安全性不如 CS 好控制
(3) 易用性, BS 好于 CS, 浏览器电脑有
(4) 扩展性, BS 相对统一,只需要写 Server
1. C: Client(客户端)
2. S: Server(服务端)
示意图
Servlet 在开发动态 WEB 工程中,得到广泛的应用,掌握好 Servlet 非常重要了, Servlet(基石)是 SpringMVC 的基础
Servlet 容器(比如:Tomcat) 加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例并调用 init()方法,init()方法只调用一次。
Servlet 容器如果遇到上面的情况会重新装载 Servlet:
当 web 应用被终止,或者 Servlet 容器终止运行,或者 Servlet 类重新装载时,会调用 destroy() 方法,比如重启 Tomcat、或者 redeploye web (重新发布)应用。
工程路径:D:\IDEA_code\xjz_javaweb\servlet
获取 Servlet 程序的 servlet-name 的值
获取初始化参数 init-param
获取 ServletContext 对象
应用实例…
示意图(思路分析)
先看一个需求: 如果我们希望统计某个 web 应用的所有 Servlet 被访问的次数,怎么办
方案 1-DB
由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过 ServletContext 对象来实现多个 Servlet 间通讯。ServletContext 对象通常也被称之为域对象。【示意图】
详情看 韩顺平JavaWeb-笔记
代码如下D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\servletcontext
运行结果
我们是OOP程序员,第一步,看它的类图(继承关系和方法)
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() 获取请求转发对象, 请求转发的核心对象
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);
}
}
获 取 doPost 参 数 中 文 乱 码 解 决 方 案 , 注 意 setCharacterEncoding(“utf-8”) 要 写 在request.getParameter()前
注意:如果通过 PrintWriter writer, 有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)
处理 http 响应数据中文乱码问题
再次理解 Http 协议响应 Content-Type 的含义, 比如 text/plain application/x-tar**
请求转发原理示意图
每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用。
HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse 对象来进行设置即可
请求重定向指:一个 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
response.sendRedirect("/servlet/downServletNew");
//第二种重定向的写法
response.setStatus(302);//设置 http响应的状态码
//设置http响应的 Location: /servlet/downServletNew
response.setHeader("Location","/servlet/downServletNew");
//5. 动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath=" + contextPath);//servlet
//response.sendRedirect("/servlet/downServletNew");
response.sendRedirect(contextPath + "/downServletNew");
HTTP响应包括3个部分
HTTP响应包分析图
D:\IDEA_code\xjz_javaweb\webpath\src\com\xjz\servlet
网址 : servlet03 表示路径
小结: 在编写资源路径时: , 考虑这么几点
(1) 这个路径 前面有没有 /
(2) 这个路径 在哪里被解析 [服务器还是浏览器] , 如果前面有 / , 并且是在 浏览器被解析的 被解析成 http://ip:port/ , 如果在服务器端被解析 , 被解析成 /工程路径/
(3) 如果这个路径,前面没有 / , 并且在浏览器被解析,则以浏览器当前的地址栏 去掉资源部分,作为一个相对路径.
(4) 这个路径,最后有没有 / , 如果最后有/ 表示路径, 如果没有 / 表示资源
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个 web 资源,然后关闭浏览器,整个过程称之为一个会话。
会话过程中要解决的一些问题?
Cookie(小甜饼)是客户端技术,服务器把每个用户的数据以 cookie 的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的 web 资源时,就会带着各自的数据去。这样,web 资源处理的就是用户各自的数据了。【简单示意图】
演示 Cookie 底层实现机制**,** 创建和读取 Cookie
D:\IDEA_code\xjz_javaweb\cookie-session\src\com\xjz\cookie\CreateCookie.java
Cookie 的生命周期指的是如何管理 Cookie 什么时候被销毁(删除)
setMaxAge()
● 正数,表示在指定的秒数后过期
● 负数,表示浏览器关闭,Cookie 就会被删除(默认值是-1)
● 0,表示马上删除 Cookie
Cookie 有效路径 Path 的设置
Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。 path属性是通过请求的地址来进行有效的过滤
规则如下
说明:如果存放中文的 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("设置中文cookie成功~
");
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") + "]
");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
● 解决之道—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 值
创建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("创建/操作 session成功~"
);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
第二次刷新后
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("读取session成功~
");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
我们可以看出,两个servlet的JSEESIONID是同一JSESSIONID
有了代码支撑,我们在回头看 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("创建session成功,设置生命周期 60s
");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
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);
}
}
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("删除session成功
");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
总结:Session 的生命周期
说明:
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>
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("密码不能为 null,请重新输入
");
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("恭喜你,管理员:"
+ usernameVal + "");
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("恭喜你,管理员:"
+ userlogin.toString() + "");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
Listener 是 JavaEE 的规范,就是接口
监听器的作用是,监听某种变化(一般就是对象创建/销毁, 属性变化), 触发对应方法完成相应的任务
JavaWeb 中的监听器(共八个), 目前最常用的是ServletContextListener
作用:监听 ServletContext 创建或销毁(当我们 Web 应用启动时,就会创建 ServletContext),即生命周期监听,应用场景(1)加载初始化的配置文件;比如 spring 的配置文件 (2)任务调度(配合定时器 Timer/TimerTask)
相关方法
作用:监听 ServletContext 属性变化
相关方法
1. 作用:监听 Session 创建或销毁,即生命周期监听
2. 相关方法
1. ServletRequestListener 监听器
2. 作用:监听 Request 创建或销毁,即 Request 生命周期监听
相关方法
1. 作用:监听 Request 属性变化
2. 相关方法
**1. Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
D:\IDEA_code\xjz_javaweb\filter
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 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在
D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\XjzFilterConfig.java
一句话: FilterChain: 在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链。
D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\AFilter.java/BFilter.java
多个 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 指的是 JavaScript 对象表示法(JavaScript Object Notation)
JSON 是轻量级的文本数据交换格式
JSON 独立于语言 [老韩解读:即 java 、php、asp.net , go 等都可以使用 JSON]
JSON 具有自我描述性,更易理解, 一句话,非常的好用…
java 中使用 json,需要引入到第 3 方的包 gson.jar
Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。
可以对 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
// ,如果同学们忘记了,回去看 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
List<Book> bookList2 = gson.fromJson(bookListStr, type);
System.out.println("bookList2= " + bookList2);
//将 json 字符串 转成 List 集合方式 2
//代码解读
//(1) 如果我们 new TypeToken>() 提示
// 'TypeToken()' has protected access in 'com.google.gson.reflect.TypeToken'
//(2) 因为 TypeToken 的无参构造器是 protected , 而 new TypeToken>() 就是调用其无参构造器
//(3) 根据java基础,如果一个方法是 protected,而且不在同一个包中,是不能直接访问的,因此报错
//(4) 为什么 new TypeToken>(){} 使用就可以,这里就涉及到 匿名内部类的知识点.
//(5) 当 new TypeToken>(){} 其实这个类型不是 TypeToken,而是一个 匿名内部类(可以理解为TypeToken的子类)
//(6) 而且这个匿名内部类是有自己的无参构造器(隐式),根据java基础规则:当执行子类的无参构造器时,默认super()
//Type type = new TypeToken>(){}.getType(); // java.util.List
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> bookMap2 = gson.fromJson(strBookMap,
new TypeToken<Map<String, Book>>() {
}.getType());
System.out.println("bookMap2= " + bookMap2);
}
}
在线文档:https://www.w3school.com.cn/js/js_ajax_intro.asp
演示 javascript 发送原生 ajax 请求的案例
在输入框输入用户名
点击验证用户名, 使用 ajax 方式, 服务端验证该用户名是否已经占用了, 如果该用户
已经占用, 以 json 格式返回该用户信息
假定用户名为 king , 就不可用, 其它用户名可以=》 后面我们接入 DB[Mysql+JDBC]
对页面进行局部刷新, 显示返回信息
小思考: 为什么直接返回用户名是否可用信息, 完成案例后, 再思考?
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 可以实现在同一个线程数据安全共享