1. Filter 的概念
Filter(过滤器),当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能
2. Filter 的作用
一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤
3. Filter 快速入门
-
使用步骤
- 定义一个类,实现接口 Filter
- 复写方法
- 配置拦截路径
- web.xml
- 注解
- 放行
-
编码实现
package com.zt.web.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") // 访问所有资源前,都会执行该过滤器 public class FilterDemo1 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("filter..."); // 放行 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
4. Filter 的细节
-
web.xml 配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.zt.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <!--拦截路径--> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
-
过滤器的执行流程
- 执行过滤器
- 执行放行后的资源
- 回来执行过滤器放行代码下边的代码
package com.zt.web.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class FilterDemo2 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 对 request 请求消息增强 System.out.println("FilterDemo2执行了"); // 放行 chain.doFilter(req, resp); // 对 response 响应消息增强 System.out.println("FilterDemo2继续执行了"); } public void init(FilterConfig config) throws ServletException { } }
执行结果:
FilterDemo2执行了 index.jsp FilterDemo2继续执行了
-
过滤器生命周期方法
- init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
- doFilter:每一次请求被拦截资源时,会执行。执行多次
- destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
package com.zt.web.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class FilterDemo3 implements Filter { /** * 在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行 destroy方法。 * 只执行一次。用于释放资源 */ public void destroy() { System.out.println("destroy..."); } /** * 每一次请求被拦截资源时,会执行。执行多次 * @param req * @param resp * @param chain * @throws ServletException * @throws IOException */ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("doFilter..."); chain.doFilter(req, resp); } /** * 在服务器启动后,会创建 Filter 对象,然后调用 init 方法。 * 只执行一次。用于加载资源 * @param config * @throws ServletException */ public void init(FilterConfig config) throws ServletException { System.out.println("init..."); } }
执行结果:
init... doFilter... index.jsp destroy...
-
过滤器配置详解
-
拦截路径的配置
- 具体资源路径:
/index.jsp
只有访问 index.jsp 资源时,过滤器才会被执行 - 拦截目录:
/user/*
访问 /user 目录下的所有资源时,过滤器都会被执行 - 后缀名拦截:
*.jsp
访问所有后缀名为 jsp 资源时,过滤器都会被执行 - 拦截所有资源:
/*
访问所有资源时,过滤器都会被执行
- 具体资源路径:
-
拦截方式的配置
-
注解配置
-
设置 dispatcherTypes 属性
- REQUEST:默认值。浏览器直接请求资源
- FORWARD:转发访问资源
- INCLUDE:包含访问资源
- ERROR:错误跳转资源
- ASYNC:异步访问资源
-
实例
package com.zt.web.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; // 浏览器直接请求 index.jsp 时,过滤器才会被执行 @WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST) public class FilterDemo4 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("FilterDemo4..."); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { } }
-
-
web.xml 配置
- 设置
<dispatcher></dispatcher>
标签- REQUEST:默认值。浏览器直接请求资源
- FORWARD:转发访问资源
- INCLUDE:包含访问资源
- ERROR:错误跳转资源
- ASYNC:异步访问资源
- 设置
-
-
-
过滤器链(配置多个过滤器)
-
过滤器执行顺序
如果有两个过滤器:过滤器1和过滤器2
- 过滤器1
- 过滤器2
- 访问资源
- 过滤器2
- 过滤器1
-
过滤器执行先后问题
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
* 如: AFilter 和 BFilter,AFilter就先执行了。 - web.xml配置:
<filter-mapping>
谁定义在上边,谁先执行
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
-
5. Filter 的案例
5.1 登录验证
-
需求
- 验证其是否登录
- 如果登录了,则直接放行。
- 如果没有登录,则跳转到登录页面,提示 “您尚未登录,请先登录” 。
-
设计
-
编码实现
package com.zt.web.filter; import com.zt.domain.Admin; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.IOException; /** * 登录验证的过滤器 */ @WebFilter("/*") public class LoginFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 1.强制转换 HttpServletRequest request = (HttpServletRequest) req; // 2.获取资源请求路径 String requestURI = request.getRequestURI(); // 3.判断是否包含登录相关的资源路径 if(requestURI.contains("/login.jsp") || requestURI.contains("/checkCodeServlet") || requestURI.contains("/loginServlet") || requestURI.contains("/css/") || requestURI.contains("/js/") || requestURI.contains("/fonts/")){ // 包含,放行 chain.doFilter(req, resp); } else { // 不包含,验证用户是否登录 HttpSession session = request.getSession(); Admin loginAdmin = (Admin) session.getAttribute("loginAdmin"); if(loginAdmin != null) { // 用户已登录,放行 chain.doFilter(req, resp); } else { // 用户未登录 request.setAttribute("login_msg","您尚未登录,请先登录!"); // 转发到登录页面 request.getRequestDispatcher("/login.jsp").forward(request,resp); } } } public void init(FilterConfig config) throws ServletException { } }
5.2 过滤敏感词汇
-
需求
- 对录入的数据进行敏感词汇过滤
- 如果是敏感词汇,替换为 ***
-
设计
-
增强对象的功能
我们使用设计模式(一些通用的解决固定问题的方式)来增强对象的功能
代理模式:
-
代理模式的概念
代理对象代理真实对象,达到增强真实对象功能的目的
-
代理模式的实现方式
- 静态代理:有一个类文件描述代理模式
- 动态代理:在内存中形成代理类
-
动态代理的实现步骤
- 代理对象和真实对象实现相同的接口
- 代理对象 = Proxy.newProxyInstance()
- 使用代理对象调用方法
- 增强方法
-
动态代理的增强方式
- 增强参数列表
- 增强返回值类型
- 增强方法体执行逻辑
-
动态代理的编码实现
练习:我在代理商处买一台联想电脑,付给代理商 8000 元,代理商花了 6800 元从联想公司买了一台电脑,并返还给我一台联想电脑加鼠标垫。
SaleComputer 接口
package com.zt.proxy; public interface SaleComputer { public String sale(double money); }
Lenovo 类
package com.zt.proxy; public class Lenovo implements SaleComputer { @Override public String sale(double money) { System.out.println("花了"+money+"买了一台电脑"); return "联想电脑"; } }
ProxyTest
package com.zt.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest { public static void main(String[] args) { // 1.创建真实对象 Lenovo lenovo = new Lenovo(); // 2.动态代理增强 lenovo 对象 /** * 三个参数: * 1.类加载器:真实对象.getClass().getClassLoader() * 2.接口数组: 真实对象.getClass().getInterfaces() * 3.处理器: new InvocationHandler() */ // 创建代理对象 SaleComputer proxy_lennovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() { /** * 代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行 * 参数: * 1.proxy:代理对象(一般不用) * 2.method:代理对象调用的方法,被封装为的对象 * 3.代理对象调用方法时,传递的实际参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 判断是否为 sale 方法 if(method.getName().equals("sale")){ // 1.增强参数 double money = (double) args[0]; money = money * 0.85; // 使用真实对象调用该方法 String obj = (String) method.invoke(lenovo, money); // 2.增强返回值 return obj+"_鼠标垫"; } else { Object obj = method.invoke(lenovo, args[0]); return obj; } } }); // 3.调用方法 String sale = proxy_lennovo.sale(8000); System.out.println(sale); } }
执行结果:
花了6800.0买了一台电脑 联想电脑_鼠标垫
-
-
编码实现
敏感词汇.txt
笨蛋 傻瓜
SensitiveWordsFilter
package com.zt.web.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; /** * 敏感词汇过滤器 */ @WebFilter("/*") public class SensitiveWordsFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 1.创建代理对象,增强 getParameter 方法 ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强 getParameter 方法 // 判断是否是 getParameter 方法 if (method.getName().equals("getParameter")) { // 增强返回值 // 获取返回值 String value = (String) method.invoke(req, args); if (value != null) { for (String str : list) { if (value.contains(str)) { value = value.replaceAll(str, "***"); } } } return value; } else { return method.invoke(req, args); } } }); // 2.放行 chain.doFilter(proxy_req, resp); } //敏感词汇集合 private List<String> list = new ArrayList<String>(); public void init(FilterConfig config) throws ServletException { try { //1.获取文件真实路径 ServletContext servletContext = config.getServletContext(); String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt"); //2.读取文件 BufferedReader br = new BufferedReader(new FileReader(realPath)); //3.将文件的每一行数据添加到list中 String line = null; while ((line = br.readLine()) != null) { list.add(line); } br.close(); } catch (Exception e) { e.printStackTrace(); } } }
TestServlet
package com.zt.web.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/TestServlet") public class TestServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); System.out.println(name); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
打开浏览器访问:
http://localhost/day17_2/TestServlet?name=笨蛋
执行结果
***