文章目录
- 过滤器是什么?
- 一、过滤器概述
- 二、过滤器工作位置图解
- 三、Filter接口API
- 四、过滤器使用
- 4.1 定义一个过滤器类,编写功能代码:
- 4.2 xml配置:
- 4.3 定义 servletG 目标资源 模拟测试 :
- 4.4 过滤图解
- 五、过滤器生命周期
- 六、过滤器链的使用
- 6.1 图解过滤链
- 6.2 测试过滤链
- 6.3 工作流程图解
- 七、注解方式配置过滤器
- 7.1 一个比较完整的Filter的XML配置
- 7.2 将xml配置转换成注解方式实现
- 总结
过滤器是什么?
Filter
,即 过滤器 ,是JAVAEE技术规范之一,- 作用目标资源的请求进行过滤的一套技术规范
- 是Java Web项目中最为实用的技术之一
客户端发送请求 先经过filter的doFilter方法,这个方法控制是否放行请求到达目标,
不放行 即 使用filter的方式返回响应给客户端;
放行之后 请求即正常到达目标servlet
放行后 还可以对 响应数据进行处理
一、过滤器概述
-
Filter接口
定义了过滤器的开发规范,所有的过滤器都要实现该接口 -
Filter
的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest
和HttpServletResponse
对象后,会先调用Filter的doFilter
方法 -
Filter的
doFilter
方法可以控制请求是否继续,- 如果放行,则请求继续,
- 如果拒绝,则请求到此为止,由过滤器本身做出响应
-
Filter
不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理 -
Filter是GOF中责任链模式的典型案例
-
Filter的常用应用包括但不限于: 登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析… …
二、过滤器工作位置图解
三、Filter接口API
- 源码
package jakarta.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
- API
API | 目标 |
---|---|
default public void init(FilterConfig filterConfig) | 初始化方法,由容器调用并传入初始配置信息filterConfig对象 |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中 |
default public void destroy() | 销毁方法,容器在回收过滤器对象之前调用的方法 |
四、过滤器使用
- 创建类 实现
Filter接口
- 重写
doFilter
方法 - 可选择调用放行方法 将请求传下去 :
3.1.filterChain.doFilter(servletRequest, servletResponse);
3.2. 不写此方法 就是不放行请求到目标资源 卡这了
目标:开发一个日志记录过滤器
- 用户请求到达目标资源之前,记录用户的请求资源路径
- 响应之前记录本次请求目标资源运算的耗时
- 可以选择将日志记录进入文件,为了方便测试,这里将日志直接在控制台打印
4.1 定义一个过滤器类,编写功能代码:
package com.doug.filters;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description:
*/
public class LoggingFilter implements Filter {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//父类转子类 能够使用子类独有的方法
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//拼接日志
String requestURI = req.getRequestURI();
String time = dateFormat.format(new Date());
String beforeLogging = requestURI + "在" + time + "被请求了";
// 打印日志
System.out.println(beforeLogging);
// 获取系统时间
long t1 = System.currentTimeMillis();
//放行请求
filterChain.doFilter(servletRequest, servletResponse);
// 获取系统时间
long t2 = System.currentTimeMillis();
// 拼接日志文本
String afterLogging = requestURI + "在" + time + "的请求耗时:" + (t2 - t1) + "毫秒";
// 打印日志
System.out.println(afterLogging);
}
}
说明
doFilter
方法中的请求和响应对象是以父接口的形式声明的,实际传入的实参就是HttpServletRequest和HttpServletResponse子接口级别的,可以安全强转filterChain.doFilter(request,response);
这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止filterChain.doFilter(request,response);
在放行时需要传入request
和response
,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象
4.2 xml配置:
<!--配置filter,并为filter起别名-->
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.doug.filters.LoggingFilter</filter-class>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<!-- 可以用获取servlet别名的方式 -->
<servlet-name>sg1</servlet-name>
<!-- 或者 使用获取servletG 路径的方式 -->
<url-pattern>/servletG</url-pattern>
<!-- 或者 通过后缀名确定过滤资源-->
<url-pattern>*.html</url-pattern>
</filter-mapping>
说明:
filter-mapping
标签中定义了过滤器对那些资源进行过滤- 子标签
url-pattern
通过映射路径确定过滤范围
/servletA
精确匹配,表示对servletA资源的请求进行过滤*.html
表示对以.action
结尾的路径进行过滤/*
表示对所有资源进行过滤- 一个
filter-mapping
下可以配置多个url-pattern- 子标签
servlet-name
通过servlet别名确定对那些servlet进行过滤
- 使用该标签确定目标资源的前提是servlet已经起了别名
- 一个
filter-mapping
下可以定义多个servlet-name- 一个
filter-mapping
下,servlet-name
和url-pattern
子标签可以同时存在
4.3 定义 servletG 目标资源 模拟测试 :
@WebServlet(value = "/servletG",name = "sg1")
public class ServletG extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("servletG处理请求的方法,耗时10毫秒");
System.out.println("servletG处理请求的方法,耗时10毫秒");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
4.4 过滤图解
五、过滤器生命周期
过滤器作为web项目的组件之一,和Servlet的生命周期类似,
略有不同,没有servlet的load-on-startup的配置,
默认就是系统启动立刻构造
阶段 | 对应方法 | 执行时机 | 执行次数 |
---|---|---|---|
创建对象 | 构造器 | web应用启动时 | 1 |
初始化方法 | void init(FilterConfig filterConfig) | 构造完毕 | 1 |
过滤请求 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) | 每次请求 | 多次 |
销毁 | default void destroy() | web应用关闭时 | 1次 |
六、过滤器链的使用
一个web项目中,可以同时定义多个过滤器,
多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,
称之为过滤器链
- 过滤器链中的过滤器的顺序由
filter-mapping
顺序决定 - 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
- 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
6.1 图解过滤链
6.2 测试过滤链
- 定义servlet
@WebServlet("/servletH")
public class ServletH extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletH service method invoked");
}
}
- 创建多个Filter
三个过滤器代码
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter1 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter1 after chain.doFilter code invoked");
}
}
public class Filter2 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter2 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter2 after chain.doFilter code invoked");
}
}
public class Filter3 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter3 before chain.doFilter code invoked");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter3 after chain.doFilter code invoked");
}
}
- xml 定义多个filtermapping 标签
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.doug.filters.Filter1</filter-class>
</filter>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.doug.filters.Filter2</filter-class>
</filter>
<filter>
<filter-name>filter3</filter-name>
<filter-class>com.doug.filters.Filter3</filter-class>
</filter>
<!--filter-mapping的顺序决定了过滤器的工作顺序-->
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/servletH</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/servletH</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter3</filter-name>
<url-pattern>/servletH</url-pattern>
</filter-mapping>
6.3 工作流程图解
七、注解方式配置过滤器
7.1 一个比较完整的Filter的XML配置
<!--配置filter,并为filter起别名-->
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.doug.filters.LoggingFilter</filter-class>
<!--配置filter的初始参数-->
<init-param>
<param-name>dateTimePattern</param-name>
<param-value>yyyy-MM-dd HH:mm:ss</param-value>
</init-param>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<!--通过映射路径确定过滤资源-->
<url-pattern>/servletA</url-pattern>
<!--通过后缀名确定过滤资源-->
<url-pattern>*.html</url-pattern>
<!--通过servlet别名确定过滤资源-->
<servlet-name>servletBName</servlet-name>
</filter-mapping>
7.2 将xml配置转换成注解方式实现
@WebFilter(
filterName = "loggingFilter",
initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")},
urlPatterns = {"/servletA","*.html"},
servletNames = {"servletBName"}
)
public class LoggingFilter implements Filter {
private SimpleDateFormat dateFormat ;
/*init初始化方法,通过filterConfig获取初始化参数
* init方法中,可以用于定义一些其他初始化功能代码
* */
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取初始参数
String dateTimePattern = filterConfig.getInitParameter("dateTimePattern");
// 初始化成员变量
dateFormat=new SimpleDateFormat(dateTimePattern);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 参数父转子
HttpServletRequest request =(HttpServletRequest) servletRequest;
HttpServletResponse response =(HttpServletResponse) servletResponse;
// 拼接日志文本
String requestURI = request.getRequestURI();
String time = dateFormat.format(new Date());
String beforeLogging =requestURI+"在"+time+"被请求了";
// 打印日志
System.out.println(beforeLogging);
// 获取系统时间
long t1 = System.currentTimeMillis();
// 放行请求
filterChain.doFilter(request,response);
// 获取系统时间
long t2 = System.currentTimeMillis();
String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒";
// 打印日志
System.out.println(afterLogging);
}
}
总结
过滤器开发中应用的场景 :
- 日志的记录
- 性能的分析
- 乱码的处理
- 事务的控制
- 登录的控制
- 跨域的处理
- … …
关于tomcat 10 在xml 配置 过滤器 导包是 jakarta
不是 'javax
会报红
但是可以正常使用…
不想爆红:
-
就降低 tomcat版本 为tomcat9
主要是servlet-api 版本问题 -
添加依赖也可以
因为以上没有使用maven 所以没有pom.xml添加依赖