1 过滤器基本概念
1.1 引入
场景1:
在servlet中获取用户参数数据 : request.getParameter("参数名") 遇到参数内容中文乱码问题,
post提交:
request.setCharacterEncoding("utf-8");
get提交:
手动解码: name = new String(name.getBytes("iso-8859-1"),"utf-8")
问题: 如何把这些重复的操作抽取出来呢?
这时就可以用到过滤器,把重复的操作代码写在过滤器中!!!
场景2:
登录页面 -> 输入用户民或密码 -》 后台检查是否成功 -》 登录成功,看到用户首页( 用户名:xxxx )把登录数据放在session域对象中(user)
用户首页如果不登录是看不到的:
判断用户登录权限代码:
HttpSession session = request.getSession(false);
if(session==null){
跳转到登陆页面
}else{
String user = (String)session.getAttribute("user");
if(user==null){
跳转到登录页面
}
}
用户资源修改页面(需要用户登录才能访问)
HttpSession session = request.getSession(false);
if(session==null){
跳转到登陆页面
}else{
String user = (String)session.getAttribute("user");
if(user==null){
跳转到登录页面
}
}
问题:如何把这些登录权限代码抽取出???
这是用到了过滤器,把这些登录权限代码写在过滤器中.
1.2 什么是过滤器?
1)过滤器就是一个Filter接口,在javax.servlet.Filter;
2)过滤器是servlet的三大组件之一:
servlet的三大组件:
2.1 (servlet) Servlet接口: javax.servlet.Servlet; 作用:用于开发动态网页
2.2 (过滤器)Filter接口: javax.servlet.Filter; 作用:???
2.3 (监听器)Listener接口: javax.servlet.*
servlet组件的特点:
1)把组件配置到web.xml文件中
2)组件就可以交给tomcat服务器运行!!!!
3)作用:
过滤器的作用,就是一个实现了Filter接口的对象,这个对象可以在请求资源(可能是动态网页或者静态网页)时,或者在响应资源时,或者在请求和响应资源时,执行过滤任务。
1.3 体验
package cn.jxufe.a_filter; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TargetServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("4)执行了servlet"); response.getWriter().write("6)用户看到响应内容"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
package cn.jxufe.a_filter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /* * 第一个过滤器 */ public class FirstFilter implements Filter { public FirstFilter() { System.out.println("1)过滤器对象创建了!"); } /** * FilterConfig对象封装了所有当前过滤器配置的初始化参数 * */ public void init(FilterConfig filterConfig) throws ServletException { System.out.println("2)过滤器初始化方法"); // /** // * 获取初始化参数 // */ // System.out.println(filterConfig.getInitParameter("AAA")); // // 遍历所有参数 // Enumeration<String> enums = filterConfig.getInitParameterNames(); // while (enums.hasMoreElements()) { // String paramName = enums.nextElement(); // String paramValue = filterConfig.getInitParameter(paramName); // System.out.println(paramName + "=" + paramValue); // } } /** * 执行过滤任务 */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("3)过滤器正在执行过滤任务-过滤请求"); // 放行 chain.doFilter(request, response); System.out.println("5)过滤器正在执行过滤任务--过滤响应"); } /** * 过滤器对象销毁的时候才会调用 * web项目重新部署或者tomcat服务器停止了才会销毁过滤器对象 */ public void destroy() { System.out.println("过滤器被销毁了"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 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>FirstFilter</filter-name> <filter-class>cn.jxufe.a_filter.FirstFilter</filter-class> </filter> <!-- 过滤器映射配置 --> <filter-mapping> <!-- 也是内部名称,但是和上面的名称保持一致!!! --> <filter-name>FirstFilter</filter-name> <!--过滤器的url-pattern代表的是过滤的路径,而不是访问过滤器的路径 --> <url-pattern>/target</url-pattern> </filter-mapping> <servlet> <servlet-name>TargetServlet</servlet-name> <servlet-class>cn.jxufe.a_filter.TargetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TargetServlet</servlet-name> <url-pattern>/target</url-pattern> </servlet-mapping> </web-app>
1.4 过滤器的生命周期
构造方法: 创建过滤器对象的时候调用。在加载当前项目的时候加载过滤器,只调用1次。单实例的多线程。
init方法: 在创建完过滤器对象之后调用。只调用1次。
doFilter方法: 过滤任务方法。会调用n次。每次访问目标资源的时候,doFilter就会被调用。
destory方法:在销毁过滤器对象的时候调用。在web项目重新部署或tomcat服务器停止的时候销毁过滤器对象。
2 映射配置
<!-- 配置一个过滤器 --> <!-- 过滤器配置 --> <filter> <!-- 内部名称 --> <filter-name>FirstFilter</filter-name> <filter-class>cn.jxufe.a_filter.FirstFilter</filter-class> </filter> <!-- 过滤器映射配置 --> <filter-mapping> <!-- 也是内部名称,但是和上面的名称保持一致!!! --> <filter-name>FirstFilter</filter-name> <!--过滤器的url-pattern代表的是过滤的路径,而不是访问过滤器的路径 --> <url-pattern>/target</url-pattern> </filter-mapping>
注意: servlet的url-pattern指的是访问servlet的路径,而过滤器的url-pattern指的是需要过滤的路径。
而这个过滤的路径,可以是任意资源路径(可以是静态网页路径,页可以是动态网页路径)。
过滤器url-pattern 访问目标资源
精确过滤 /target http://localhsot:8080/day20/target
/index.html http://localhsot:8080/day20/index.html
模糊过滤 /* http://localhsot:8080/day20/任意路径
/itcast/* http://localhsot:8080/day20/itcast/任意路径
*.后缀名 http://localhsot:8080/day20/任意路径.后缀名
(*.do) ( http://localhsot:8080/day20/任意路径.do)
3 FilteConfig对象
3.1 简介
和servletconfig对象类似,这个FilterConfig对象加载初始化参数内容
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 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>FirstFilter</filter-name> <filter-class>cn.jxufe.a_filter.FirstFilter</filter-class> <!-- 初始化参数 --> <init-param> <param-name>AAA</param-name> <param-value>AAA' value</param-value> </init-param> <init-param> <param-name>BBB</param-name> <param-value>BBB' value</param-value> </init-param> </filter> <!-- 过滤器映射配置 --> <filter-mapping> <!-- 也是内部名称,但是和上面的名称保持一致!!! --> <filter-name>FirstFilter</filter-name> <!--过滤器的url-pattern代表的是过滤的路径,而不是访问过滤器的路径 --> <url-pattern>/target</url-pattern> </filter-mapping> <servlet> <servlet-name>TargetServlet</servlet-name> <servlet-class>cn.jxufe.a_filter.TargetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TargetServlet</servlet-name> <url-pattern>/target</url-pattern> </servlet-mapping> </web-app>
package cn.jxufe.a_filter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /* * 第一个过滤器 */ public class FirstFilter implements Filter { public FirstFilter() { System.out.println("1)过滤器对象创建了!"); } /** * FilterConfig对象封装了所有当前过滤器配置的初始化参数 * */ public void init(FilterConfig filterConfig) throws ServletException { System.out.println("2)过滤器初始化方法"); /** * 获取初始化参数 */ System.out.println(filterConfig.getInitParameter("AAA")); // 遍历所有参数 Enumeration<String> enums = filterConfig.getInitParameterNames(); while (enums.hasMoreElements()) { String paramName = enums.nextElement(); String paramValue = filterConfig.getInitParameter(paramName); System.out.println(paramName + "=" + paramValue); } } /** * 执行过滤任务 */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("3)过滤器正在执行过滤任务-过滤请求"); // 放行 chain.doFilter(request, response); System.out.println("5)过滤器正在执行过滤任务--过滤响应"); } /** * 过滤器对象销毁的时候才会调用 * web项目重新部署或者tomcat服务器停止了才会销毁过滤器对象 */ public void destroy() { System.out.println("过滤器被销毁了"); } }
4 FilterChain对象
4.1 简介
FilterChain
对象叫做过滤器链对象。
什么是过滤器链? 当一个资源被多个过滤器所过滤,那么就形成了一个过滤器链。
<filter> <filter-name>First</filter-name> <filter-class>cn.jxufe.b_chain.FirstFilter</filter-class> </filter> <filter> <filter-name>Second</filter-name> <filter-class>cn.jxufe.b_chain.SecondFilter</filter-class> </filter> <filter-mapping> <filter-name>First</filter-name> <url-pattern>/first</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Second</filter-name> <url-pattern>/first</url-pattern> </filter-mapping>
package cn.jxufe.b_chain; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FirstServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("3)执行目标资源代码"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package cn.jxufe.b_chain; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class FirstFilter implements Filter { @Override public void destroy() { // TODO Auto-gene rated method stub } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub System.out.println("1)执行第一个过滤器的请求过滤"); // 放行 /** * FilterChian就是过滤器链对象,其中这个doFilter方法作用是把请求或响应交给下一个过滤器,如果没有下一个过滤器,就访问到目标资源了或返回给用户显示。 * 过滤器链的过滤执行顺序由web.xml文件中过滤器的filter-mapping的顺序决定的,先配置的先被访问到!!!! * */ chain.doFilter(request, response); System.out.println("5)执行第二个过滤器的响应过滤"); } }
package cn.jxufe.b_chain; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class SecondFilter implements Filter { @Override public void destroy() { // TODO Auto-gene rated method stub } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub System.out.println("2)执行第二个过滤器的请求过滤"); // 放行 /** * FilterChian就是过滤器链对象,其中这个doFilter方法作用是把请求或响应交给下一个过滤器,如果没有下一个过滤器,就访问到目标资源了或返回给用户显示。 * 过滤器链的过滤执行顺序由web.xml文件中过滤器的filter-mapping的顺序决定的,先配置的先被访问到!!!! * */ chain.doFilter(request, response); System.out.println("4)执行第二个过滤器的响应过滤"); } }
5
装饰者模式
1)编写一个BufferedReader装饰者类,继承被装饰者类。(不能是final的) 2)在装饰类中定义一个成员变量,用于接收被装饰者类的对象。 3)在装饰者类的构造方法中传入被装饰者类,使用第二步定义的变量接收被转入的 被装饰者类。 4)在装饰类类中重写被装饰者类方法,对其方法进行增强。
aaa.txt 123 abc ABC
需求:对于以上文件输出时,每行增加一个序号和:
package cn.jxufe.c_decorator; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.Reader; /* * 装饰者模式 */ public class Demo01 { /** *需求:使用BufferedReader读取一个文件内容 * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub BufferedReader b = new BufferedReader(new FileReader("e:/aaa.txt")); BufferedReader br = new MyBufferedReader(b); String str = null; while ((str = br.readLine()) != null) { System.out.println(str); } } /** * 改造: 在每行名字前面加上一个序号。 * 解决方案: 希望readLine方法返回给我们的就是一个带序号的内容。对BufferedReader的readLine方法进行增强。这是就可以用到装饰者模式。 * 装饰者模式的开发步骤: * 概念: 装饰者 和 被装饰者 . BufferedReader就是被装饰者 * 1)编写一个BufferedReader装饰者类,继承被装饰者类。(不能是final的) * 2)在装饰类中定义一个成员变量,用于接收被装饰者类的对象。 * 3)在装饰者类的构造方法中传入被装饰者类,使用第二步定义的变量接收被转入的 被装饰者类。 * 4)在装饰类类中重写被装饰者类方法,对其方法进行增强。 */ } /** * 1)编写一个BufferedReader装饰者类,继承被装饰者类。(不能是final的) * BufferedReader(是非final的) */ class MyBufferedReader extends BufferedReader { /*public MyBufferedReader(){ super(); }*/ /** * 2)在装饰类中定义一个成员变量,用于接收被装饰者类的对象。 */ private BufferedReader br; /** * 3)在装饰者类的构造方法中传入被装饰者类,使用第二步定义的变量接收被转入的 被装饰者类。 * @param in */ public MyBufferedReader(Reader in) { super(in); this.br = (BufferedReader) in; } int count = 1; /** * 4)在装饰类类中重写被装饰者类方法,对其方法进行增强。 */ @Override public String readLine() throws IOException { // 得到原来的真实的内容 String content = br.readLine(); // 加强: 加上序号 if (content != null) { content = count + ":" + content; count++; } return content; } }
6 案例- 压缩网页内容
用户浏览一个网页:
服务器-> 发送一个网页的内容给用户(1k)
一个用户一天访问10页: 服务器输出10kb内容
网站1天10万用户:
100000 * 10kb = 1 000 000kb = 1GB
消耗网络带宽。
服务器(PC机):
存放网站(网页)
服务器收费的:
按流量收费的。如果尽量减少服务器向用户输出数据量,从而减少消耗带宽。
要求:在不影响用户浏览效果前提下,减少服务器输出的数据???
这时就要用到网页内容的压缩技术!!!!
压缩网页的技术: gzip压缩技术
GZIPOutputStream类进行网页内容压缩
package cn.jxufe.c_cases; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ContentServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 准备内容 StringBuffer sb = new StringBuffer(); for (int i = 1; i <= 3000; i++) { sb.append("abcd"); } System.out.println("压缩前的数据大小: " + sb.toString().getBytes().length); //// 12000kb /** * 对网页内容进行gzip格式进行压缩 */ // 1)创建一个临时缓存容器 ByteArrayOutputStream buf = new ByteArrayOutputStream(); // 2)创建GZIPOutputStream GZIPOutputStream gzip = new GZIPOutputStream(buf); // 3)进行压缩 gzip.write(sb.toString().getBytes()); // 4)调用结束方法,把缓存内容刷新 gzip.finish(); // 5)得到压缩后的内容 byte[] result = buf.toByteArray(); System.out.println("压缩后的数据大小:" + result.length); /** * 注意:现在的内容已经是经过gzip算法压缩,必须要告诉浏览器目前输出的内容是gzip压缩格式的内容 */ response.setHeader("content-encoding", "gzip"); // 输出到浏览器 response.getOutputStream().write(result); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
使用过滤器
package cn.jxufe.c_cases; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ContentServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 2)执行目标资源代码 // 准备内容 StringBuffer sb = new StringBuffer(); for (int i = 1; i <= 3000; i++) { sb.append("abcd"); } /** * 注意: * 每次写出的网页内容都是已经经过gzip压缩的内容 * * 思路: * 1)现在response对象的getWriter()方法得到的是没有缓冲功能的PrintWriter(), * 调用write()方法就是直接把内容输出到浏览器显示。 * 2)如果我们通过改造response对象的getWriter方法,从而得到一个带有缓存功能的PrintWriter对象, * 那么write写出的网页内容就是写出到PrintWriter缓存区中,我们就可以从PrintWriter的缓存区中得到网页内容 * */ response.getWriter().write(sb.toString()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package cn.jxufe.c_cases; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; /* * 集中对网页内容进行gzip压缩 */ public class GzipFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub // 1)过滤请求 // 创建一个response的装饰者对象 MyHttpResponse myResponse = new MyHttpResponse((HttpServletResponse) response); /** * 放行 */ chain.doFilter(request, myResponse); System.out.println("放行!!!"); // 3)过滤响应 // 从缓存容器对象得到压缩前的内容 // 注意:response对象中没有方法获取实体内容,怎么办? char[] content = myResponse.getCharArray(); // gzip压缩 ByteArrayOutputStream buf = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(buf); gzip.write(new String(content).getBytes()); gzip.finish(); byte[] result = buf.toByteArray(); // 告诉浏览器发送内容的压缩格式 myResponse.setHeader("content-encoding", "gzip"); // 输出 response.getOutputStream().write(result); // myRresponse.getWriter().write(new String(result,0,result.length)); } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } } /** * HttpServletResponse的装饰者类 */ class MyHttpResponse extends HttpServletResponseWrapper { private HttpServletResponse response; // 定义一个缓存容器对象 private CharArrayWriter charArray = new CharArrayWriter(); /** * 提供一个获取charArray内容的方法(包含网页内容) * @param response */ public char[] getCharArray() { return charArray.toCharArray(); } public MyHttpResponse(HttpServletResponse response) { super(response); this.response = response; } /** * 重写getWriter()方法,让其返回一个带缓存功能的PrintWriter */ @Override public PrintWriter getWriter() throws IOException { /** * 现在已经创建了一个带CharArrayWriter缓存容器的PrintWriter了, * 如果我们调用带缓存PrintWriter对象的write()方法,那么内容会直接写入到CharrArrayWriter缓存容器中。 */ return new PrintWriter(charArray); } }
<!-- 网页内容压缩过滤器 --> <filter> <filter-name>GzipFilter</filter-name> <filter-class>cn.jxufe.c_cases.GzipFilter</filter-class> </filter> <filter-mapping> <filter-name>GzipFilter</filter-name> <url-pattern>/ContentServlet</url-pattern> </filter-mapping>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)