目录
2.2.2 编写一个过滤器Filter:AdminFilter implement Filter
2.2.3 编写一个用户处理登录的动态资源处理器Servlet:LoginServlet extends HttpServlet
2.2.4 在web.xml文件中配置Filter的拦截路径和Servlet的访问路径
5.3 在web项目中若有多个filter,则它们之间的执行流程如下图-重要
5.4 在web项目中若有多个filter:代码演示-项目结构
5.4.1 编写FIlter过滤器:Filter1和Filter2
5.4.2 编写FIlter过滤器拦截的请求资源:target.jsp
5.4.3 在web.xml文件中配置FIlter过滤器的拦截路径
7.3 ThreadLocal的简单介绍:先使用Hashtable做演示
7.4 ThreadLocal的简单介绍:再使用ThreadLocal做演示
1、什么是Filter过滤器-API全集
(1)Filter过滤器,是JavaWeb的三大基础组件之一:Filter过滤器,Servlet动态处理器,Listener监听器。
(2)Filter过滤器是JavaEE的规范,也就是一个接口。
(3)Filter过滤器的作用:拦截请求;过滤响应。
(4)拦截请求常见的应用场景是:权限检查、日志操作、事务管理 .......
2、Filter的初体验
2.1 Filter过滤器的初体验之要求
要求:在你的web工程下,有一个admin目录。在这个目录下的所有资源(html页面、jpg图片、jsp文件等)都必须是用户登录之后才允许访问。
思考:根据之前学过的内容可以知道,我们知道,用户登录之后都会把用户登录的信息保存早Session域中。
所以:要检查用户是否登录了,只需要判断Session域数据中是否包含有用户登录的信息即可。
2.2 项目结构
2.2.1 在项目的web目录下放置需要访问的资源
2.2.2 编写一个过滤器Filter:AdminFilter implement Filter
package com.wind.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @Description: 写一个过滤器
*/
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("AdminFilter init...");
}
/**
* doFilter()方法专门用于拦截请求,拦截请求之后,可以对这个请求做一些额外的操作,比如权限检查等。
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
if (session != null) {
Object user = session.getAttribute("username");
if (user == null) {
//如果等于null,表示用户还没有登录,需要给用户跳转到登录页面,注意这里是转发
servletRequest.getRequestDispatcher("/admin/login.jsp").forward(servletRequest, servletResponse);
} else {
//如果不等于null,则表示用户已经登录了,可以让用户继续往下往下走流程,访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
@Override
public void destroy() {
System.out.println("AdminFilter destroy...");
}
}
2.2.3 编写一个用户处理登录的动态资源处理器Servlet:LoginServlet extends HttpServlet
package com.wind.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
功能:用于处理登录的servlet处理器
*/
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 2655616816599866422L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("abc".equals(username) && "123".equals(password)) {
req.getSession().setAttribute("username", username);
resp.getWriter().write("登录成功");
} else {
req.getRequestDispatcher("/admin/login.jsp").forward(req, resp);
}
}
}
2.2.4 在web.xml文件中配置Filter的拦截路径和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">
<!--给AdminFilter过滤器配置拦截路径-->
<!--filter标签:用于配置一个过滤器filter-->
<filter>
<!--filter-name标签:给filter起一个别名-->
<filter-name>AdminFilter</filter-name>
<!--filter-class标签:配置filter的全类名-->
<filter-class>com.wind.filter.AdminFilter</filter-class>
</filter>
<!--filter-mapping标签:配置过滤器filter的拦截路径-->
<filter-mapping>
<!--filter-name标签:表示当前的拦截路径给哪个filter使用-->
<filter-name>AdminFilter</filter-name>
<!--url-pattern标签:配置拦截路径-->
<!--
/ 斜杠:表示请求地址是:http://ip:port/工程路径/,映射到IDEA的"web"目录
/admin/*:表示请求地址是:http://ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.wind.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
</web-app>
2.2.5 前端页面:a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<b>我是a.html文件</b>
</body>
</html>
2.2.6 前端页面:a.jsp
<%--
Created by IntelliJ IDEA.
User: cmm
Date: 2020/12/24
Time: 17:42
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<b>我是a.jsp文件</b>
<%
System.out.println("a.jsp页面执行了");
Object user = session.getAttribute("user");
//如果等于null,则说明还没有登录,于是,服务器端转发到登录页面
if (user == null) {
request.getRequestDispatcher("/admin/login.jsp").forward(request, response);
return;
}
%>
</body>
</html>
2.2.7 前端登录页面:login.jsp
<%--
Created by IntelliJ IDEA.
User: cmm
Date: 2020/12/24
Time: 18:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<b>这是登录页面,login.jsp</b><br>
<form action="http://localhost:8080/15_filter/loginServlet" method="get">
用户名:<input type="text" name="username"/><br>
密 码:<input type="password" name="password"/><br>
<input type="submit" name="登录"/><br>
</form>
</body>
</html>
2.2.8 前端发起请求做测试
(1)只要HTTP请求是访问了admin目录下的资源,都会被AdminFilter过滤器拦截住,并且转发到登录页面。
(2)在登录页面,输入错误的用户名和密码,也还是会被AdminFilter过滤器拦截住,在AdminFilter过滤器中判断用户名和密码是否都正确,发现不正确,所以还是会转发到登录页面。
(3)在登录页面,输入正确的用户名和密码,也还是会被AdminFilter过滤器拦截住,在AdminFilter过滤器中判断用户名和密码是否都正确,发现都正确,展示”登录成功“。
(4)此时,再有HTTP请求访问admin目录下的资源时,虽然都会被AdminFilter过滤器拦截住,但是用户名和密码都检验通过了,所以会继续往下执行,也就是去获取用户想要访问的资源。
2.3 FIlter过滤器的使用步骤
3、Filter的生命周期
Filter过滤器的生命周期中,包含这几个方法:
(1)构造器方法。(2)init()初始化方法。
第一步和第二步,在web工程启动的时候,就执行了(此时会创建好Filter过滤器)。
(3)doFilter过滤方法。
第三步,每次拦截到请求的时候就会执行。
(4)destroy()销毁方法。
第四步,停止web工程的时候就会执行(停止web工程,也会销毁Filter过滤器)。
4、FilterConfig类-API全集
5、FilterChain过滤器链
5.1 FilterChain接口的API全集
5.2 FilterChain接口的方法详解
5.3 在web项目中若有多个filter,则它们之间的执行流程如下图-重要
5.4 在web项目中若有多个filter:代码演示-项目结构
5.4.1 编写FIlter过滤器:Filter1和Filter2
package com.wind.filter;
import javax.servlet.*;
import java.io.IOException;
/**
Filter1
*/
public class Filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter1 前置业务执行");
System.out.println("Filter1 线程的后置=" + Thread.currentThread().getName());
//在filter1中设置一个值
servletRequest.setAttribute("key1", "value1");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter1 线程的后置=" + Thread.currentThread().getName());
System.out.println("Filter1 后置业务执行");
}
@Override
public void destroy() {
}
}
package com.wind.filter;
import javax.servlet.*;
import java.io.IOException;
/**
Filter2
*/
public class Filter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter2 前置业务执行");
System.out.println("Filter2 线程的前置=" + Thread.currentThread().getName());
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter2 线程的后置=" + Thread.currentThread().getName());
System.out.println("Filter2 后置业务执行");
//在filter2中可以获取在filter1中设置的那个值
System.out.println("在filter2中获取在filter1中设置的key1对应的value=" + servletRequest.getAttribute("key1"));
}
@Override
public void destroy() {
}
}
5.4.2 编写FIlter过滤器拦截的请求资源:target.jsp
<%--
Created by IntelliJ IDEA.
Date: 2020/12/24
Time: 22:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
System.out.println("target.jsp 业务执行");
System.out.println("target.jsp 线程=" + Thread.currentThread().getName());
%>
</body>
</html>
5.4.3 在web.xml文件中配置FIlter过滤器的拦截路径
<?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">
<!--给AdminFilter过滤器配置拦截路径-->
<!--filter标签:用于配置一个过滤器filter-->
<filter>
<!--filter-name标签:给filter起一个别名-->
<filter-name>AdminFilter</filter-name>
<!--filter-class标签:配置filter的全类名-->
<filter-class>com.wind.filter.AdminFilter</filter-class>
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
</filter>
<!--filter-mapping标签:配置过滤器filter的拦截路径-->
<filter-mapping>
<!--filter-name标签:表示当前的拦截路径给哪个filter使用-->
<filter-name>AdminFilter</filter-name>
<!--url-pattern标签:配置拦截路径-->
<!--
/ 斜杠:表示请求地址是:http://ip:port/工程路径/,映射到IDEA的"web"目录
/admin/*:表示请求地址是:http://ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>Filter1</filter-name>
<filter-class>com.wind.filter.Filter1</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter1</filter-name>
<url-pattern>/admin/target.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>Filter2</filter-name>
<filter-class>com.wind.filter.Filter2</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter2</filter-name>
<url-pattern>/admin/target.jsp</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.wind.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
</web-app>
5.4.4 前端页面发起请求做测试并看结果
6、Filter的拦截路径:精确匹配-目录匹配-后缀名匹配
7、ThreadLocal的使用
7.1 ThreadLocal的官方API介绍
7.2 ThreadLocal的简单介绍
7.3 ThreadLocal的简单介绍:先使用Hashtable做演示
7.3.1 ThreadLocalTest 类
package com.wind.threadLocal;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
/**
public static Map<String, Object> date = new Hashtable<>();
*/
public class ThreadLocalTest {
public static Map<String, Object> date = new Hashtable<>();
private static Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
//1.在run方法中随机生成一个变量,这个变量是线程需要关联的数据,然后以当前线程名作为key存储到map中
//随机数是:0-999
Integer integer = random.nextInt(1000);
String name = Thread.currentThread().getName();
System.out.println("线程[" + name + "]" + "生成的随机数是=" + integer);
date.put(name, integer);
try {
Thread.sleep(2000);
new OrderService().createOrder();
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.在线程执行结束之前,以当前线程名作为key从map中取出数据,查看是否能够取出
Object o = date.get(name);
System.out.println("在线程[" + name + "]" + "在结束时数据的数据是=" + o);
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new Task()).start();
}
}
}
7.3.2 OrderService 类
package com.wind.threadLocal;
import java.io.Serializable;
public class OrderService implements Serializable {
private static final long serialVersionUID = -6969950483259564262L;
public void createOrder() {
String name = Thread.currentThread().getName();
System.out.println("OrderService 当前线程[" + name + "]" + "中保存的数据是=" + ThreadLocalTest.date.get(name));
new OrderDao().saveOrder();
}
}
7.3.3 OrderDao 类
package com.wind.threadLocal;
import java.io.Serializable;
public class OrderDao implements Serializable {
private static final long serialVersionUID = -6969950483259564262L;
public void saveOrder() {
String name = Thread.currentThread().getName();
System.out.println("OrderDao 当前线程[" + name + "]" + "中保存的数据是=" + ThreadLocalTest.date.get(name));
}
}
7.3.4 演示结果
7.4 ThreadLocal的简单介绍:再使用ThreadLocal做演示
7.4.1 ThreadLocalTest2 类
package com.wind.threadLocal;
import java.util.Random;
/**
public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
*/
public class ThreadLocalTest2 {
public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
private static Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
//1.在run方法中随机生成一个变量,这个变量是线程需要关联的数据,然后以当前线程名作为key存储到map中
//随机数是:0-999
Integer integer = random.nextInt(1000);
String name = Thread.currentThread().getName();
System.out.println("线程[" + name + "]" + "生成的随机数是=" + integer);
threadLocal.set(integer);
try {
Thread.sleep(2000);
new OrderService2().createOrder2();
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.在线程执行结束之前,以当前线程名作为key从map中取出数据,查看是否能够取出
Object o = threadLocal.get();
System.out.println("在线程[" + name + "]" + "在结束时数据的数据是=" + o);
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new Task()).start();
}
}
}
7.4.2 OrderService2 类
package com.wind.threadLocal;
import java.io.Serializable;
public class OrderService2 implements Serializable {
private static final long serialVersionUID = -6969950483259564262L;
public void createOrder2() {
String name = Thread.currentThread().getName();
System.out.println("OrderService2 当前线程[" + name + "]" + "中保存的数据是=" + ThreadLocalTest2.threadLocal.get());
new OrderDao2().saveOrder2();
}
}
7.4.3 OrderDao2 类
package com.wind.threadLocal;
import java.io.Serializable;
public class OrderDao2 implements Serializable {
private static final long serialVersionUID = -6969950483259564262L;
public void saveOrder2() {
String name = Thread.currentThread().getName();
System.out.println("OrderDao2 当前线程[" + name + "]" + "中保存的数据是=" + ThreadLocalTest2.threadLocal.get());
}
}