JavaWeb--Listener与Filter
JavaWeb--Listener与Filter
Listener
基本概念
Listener监听器它是JavaWeb的三大组件之一,JavaWeb的三大组件分别是:Servlet程序、Listener监听器、Filter过滤器
Listener是JavaEE的规范,就是接口
监听器的作用是,监听某种变化(一般就是对象创建/销毁,属性变化),触发对应方法完成相应的任务
JavaWeb中的监听器(共八个),目前最常用的是ServletContextListener
监听接口
ServletContextListener
-
作用:监听ServletContext创建或销毁(当我们Web应用启动时,就会创建ServletContext),即生命周期监听,应用场景(1)加载初始化的配置文件;比如spring的配置文件(2)任务调度(配合定时器Timer/TimerTask)
-
方法:
- voidcontextInitialized(ServletContextEventsce)创建Servletcontext时触发
- voidcontextDestroyed(ServletContextEventsce)销毁Servletcontext时触发
-
应用:
/** * 1. 当一个类实现了 ServletContextListener * 2. 该类就是一个监听器 * 3. 该类可以监听的事件 由该类实现的监听接口决定 ,比如 实现ServletContextListener * , 则该类就可以监听 ServletContext对象的创建和销毁, 以此类推 * 4. HspServletContextListener 就是一个监听者 * 5. 当web应用启动时,就会产生 ServletContextEvent 事件, 会调用监听器的对应事件处理方法 * contextInitialized, 同时会传递 事件对象 * 6. 程序员可以通过 ServletContextEvent 事件对象,来获取需要的信息, 然后再进行业务处理 * 7. tomcat怎么知道这个监听器存在 ? 因为我们需要在web.xml中配置 */ public class HspServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); System.out.println("HspServletContextListener 监听到 " + servletContext + " 被创建.."); //如果我们获取到ServletContext 对象..进行业务处理 } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); System.out.println("HspServletContextListener 监听到 " + servletContext + " 被销毁.."); //比如可以对servletContext 数据进行处理, 或者日志的管理... System.out.println("进行处理工作....."); } } ---- web.xml <!--配置监听器--> <listener> <listener-class>com.hspedu.listener.HspServletContextListener</listener-class> </listener>
ServletContextAttributeListener
-
作用:监听ServletContext属性变化
-
方法:
- void attributeAdded(ServletContextAttributeEvent event)添加属性时调用
- void attributeReplaced(ServletContextAttributeEvent event)替换属性时调用
- void attributeRemoved(ServletContextAttributeEvent event)移除属性时调用
-
应用:
public class HspServletContextAttributeListener implements ServletContextAttributeListener { @Override public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("HspServletContextAttributeListener 监听到添加属性.." + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() ); } @Override public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("HspServletContextAttributeListener 监听到删除属性.." + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() ); } @Override public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) { System.out.println("HspServletContextAttributeListener 监听到修改属性.." + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() ); } } ----- web.xml <listener> <listener-class>com.hspedu.listener.HspServletContextAttributeListener</listener-class> </listener>
HttpSessionListener
-
作用:监听Session创建或销毁,即生命周期监听
-
方法:
- void sessionCreated(HttpSessionEvent se)创 建session时 调 用
- void sessionDestroyed(HttpSessionEvent se)销毁session时调用
-
应用:
public class HspHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); //当session创建时,我们给它设置一个生命周期 30s session.setMaxInactiveInterval(30); System.out.println("HspHttpSessionListener 监听到 session创建= " + session.getId()); System.out.println("用户id=" + session.getId() + " 上线"); } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); System.out.println("HspHttpSessionListener 监听到 session销毁= " + session.getId()); System.out.println("用户id=" + session.getId() + " 离线"); } } ------- web.xml <listener> <listener-class>com.hspedu.listener.HspHttpSessionListener</listener-class> </listener>
HttpSessionAttributeListener
- 作用:监听Session属性的变化
- 方法:
- void attributeAdded(ServletRequestAttributeEvent srae)添加属性时
- void attributeReplaced(ServletRequestAttributeEvent srae)替换属性时
- void attributeRemoved(ServletRequestAttributeEvent srae)移除属性时
- 应用:同上
ServletRequestListener
- 作用:监听Request创建或销毁,即Request生命周期监听
- 方法:
- void requestInitialized(ServletRequestEvent sre)创建request时
- void requestDestroyed(ServletRequestEvent sre)销毁request时
- 应用:同上
ServletRequestAttributeListener
- 作用:监听Request属性变化
- 方法:
- void attributeAdded(ServletRequestAttributeEvent srae)添加属性时
- void attributeReplaced(ServletRequestAttributeEvent srae)替换属性时
- void attributeRemoved(ServletRequestAttributeEvent srae)移除属性时
- 应用:同上
HttpSessionBindingListener感知监听器
HttpSessionActivationListener感知监听器
Filter
意义
概念
Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
Filter 过滤器是 JavaEE 的规范,是接口
Filter 过滤器它的作用是:拦截请求,过滤响应
应用场景:1权限检查 2.日记操作 3.事务管理
原理
应用
/**
* 1. filter在web项目启动时, 由tomcat 来创建filter实例, 只会创建一个
* 2. 会调用filter默认的无参构造器, 同时会调用 init方法, 只会调用一次
* 3. 在创建filter实例时,同时会创建一个FilterConfig对象,并通过init方法传入
* 4. 通过FilterConfig对象,程序员可以获取该filter的相关配置信息
* 5. 当一个http请求和该filter的的url-patter匹配时,就会调用doFilter方法
* 6. 在调用doFilter方法时,tomcat会同时创建ServletRequest 和 ServletResponse 和 FilterChain对象, 并通过doFilter传入.
* 7. 如果后面的请求目标资源(jsp,servlet..) 会使用到request,和 response,那么会继续传递
* 8. 的提醒:到javaweb - ssm - springboot , 有 浏览器和 web服务器(tomcat)参与, 而这两个部分不是我们程序员自己写,所以理解起来比 java se要困难!!!
*/
public class ManageFilter implements Filter {
private int count = 0;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//当Tomcat 创建 Filter创建,就会调用该方法,进行初始化
//提醒:回忆我们自己实现tomcat底层机制+servlet程序, 就会了然
//
System.out.println("ManageFilter init被调用...");
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("ManageFilter doFilter() 被调用=" + (++count));
//到每次调用该filter时,doFilter就会被调用
//如果这里,没有调用继续请求的方法,则就停止
//如果继续访问目标资源-> 等价于放行
//老师说明:在调用过滤器前,servletRequest对象=request已经被创建并封装
//所以:我们这里就可以通过servletRequest获取很多信息, 比如访问url , session
//比如访问的参数 ... 就可以做事务管理,数据获取,日志管理等
//获取到session
//可以继续使用 httpServletRequest 方法.
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
System.out.println("输入密码=" + httpServletRequest.getParameter("password"));
HttpSession session = httpServletRequest.getSession();
//获取username session对象, 还可以继续使用
Object username = session.getAttribute("username");
if (username != null) {
//解读filterChain.doFilter(servletRequest, servletResponse)
//1. 继续访问目标资源url
//2. servletRequest 和 servletResponse 对象会传递给目标资源/文件
//3. 一定要理解filter传递的两个对象,再后面的servlet/jsp 是同一个对象(指的是在一次http请求)
System.out.println("servletRequest=" + servletRequest);
System.out.println("日志信息==");
System.out.println("访问的用户名=" + username.toString());
System.out.println("访问的url=" + httpServletRequest.getRequestURL());
System.out.println("访问的IP=" + httpServletRequest.getRemoteAddr());
filterChain.doFilter(servletRequest, servletResponse);
} else {//说明没有登录过..回到登录页面
servletRequest.getRequestDispatcher("/login.jsp").
forward(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
//当filter被销毁时,会调用该方法
System.out.println("ManageFilter destroy()被调用..");
}
}
------------------
web.xml
<!--解读:filter一般写在其它servlet的前面
1. 观察我们发现filter 配置和 servlet 非常相似. filter也是被tomcat管理和维护
2. url-pattern 就是当请求的url 和 匹配的时候,就会调用该filter
3. /manage/* 第一个 / 解析成 http://ip:port/工程路径
4. 完整的路径就是 http://ip:port/工程路径/manage/* 当请求的资源url满足该条件时
都会调用filter , /manage/admin.jsp
-->
<filter>
<filter-name>ManageFilter</filter-name>
<filter-class>com.hspedu.filter.ManageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManageFilter</filter-name>
<url-pattern>/manage/*</url-pattern>
</filter-mapping>
url-pattern
-
url-pattern : Filter 的拦截路径, 即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
-
精确匹配
/a.jsp 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截 -
目录匹配
/manage/* 对应的 请求地址 http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截 -
后缀名匹配
*.jsp 后缀名可变,比如 *.action *.do 等等对应的请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截 -
Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在
生命周期
FilterConfig
- FilterConfig 是 Filter 过滤器的配置类
- Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
- FilterConfig 对象作用是获取 filter 过滤器的配置内容
应用
public class HspFilterConfig implements Filter {
private String ip; //从配置获取的ip
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("HspFilterConfig init() 被调用..");
//通过filterConfig 获取相关的参数
String filterName = filterConfig.getFilterName();
ip = filterConfig.getInitParameter("ip");
ServletContext servletContext = filterConfig.getServletContext();
//可以获取到该filter所有的配置参数名
Enumeration<String> initParameterNames =
filterConfig.getInitParameterNames();
//遍历枚举
while (initParameterNames.hasMoreElements()) {
System.out.println("名字=" + initParameterNames.nextElement());
}
System.out.println("filterName= " + filterName);
System.out.println("ip= " + ip);
System.out.println("servletContext= " + servletContext);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//通过forbidden ip 来进行控制
//先获取到访问ip
String remoteAddr = servletRequest.getRemoteAddr();
if(remoteAddr.contains(ip)) {
System.out.println("封杀该网段..");
servletRequest.getRequestDispatcher("/login.jsp").
forward(servletRequest,servletResponse);
return; //直接返回
}
//继续访问目标资源
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
--------------------
web.xml
<filter>
<filter-name>HspFilterConfig</filter-name>
<filter-class>com.hspedu.filter.HspFilterConfig</filter-class>
<!--这里就是给该filter配置的参数-有程序员根据业务逻辑来设置-->
<init-param>
<param-name>ip</param-name>
<param-value>127.0</param-value>
</init-param>
<init-param>
<param-name>port</param-name>
<param-value>8888</param-value>
</init-param>
<init-param>
<param-name>email</param-name>
<param-value>hsp@sohu.com</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HspFilterConfig</filter-name>
<url-pattern>/abc/*</url-pattern>
</filter-mapping>
FilterChain过滤器链
一句话: FilterChain:在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链
原理
应用
public class AFilter 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("AFilter---> 线程id=" +
Thread.currentThread().getId());
System.out.println("AFilter doFilter 的前置代码...");
System.out.println("执行 AFilter doFilter()");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("AFilter doFilter 的后置代码...");
}
@Override
public void destroy() {
}
}
public class BFilter 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("BFilter---> 线程id=" +
Thread.currentThread().getId());
System.out.println("BFilter doFilter 的前置代码...");
System.out.println("执行 BFilter doFilter()");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("BFilter doFilter 的后置代码...");
}
@Override
public void destroy() {
}
}
------------------------
web.xml
<filter>
<filter-name>AFilter</filter-name>
<filter-class>com.hspedu.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>BFilter</filter-name>
<filter-class>com.hspedu.filter.BFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
注意事项
- 多个 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过滤器后置代码 ->返回给浏览器页面/数据
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?