1. Filter原理
1.1 基本概念
- Filter本意为”过滤“的含义,是JavaWeb的三大组件之一,三大组件为:Servlet、Filter、
Listener。 - 过滤器是向 Web 应用程序的请求和响应处理添加功能的 Web 服务组件。
- 过滤器相当于浏览器与Web资源之间的一道过滤网,在访问资源之前通过一系列的过滤器对请求
进行修改、判断以及拦截等,也可以对响应进行修改、判断以及拦截等
1.2 工作原理
1.3 Filter使用方法
-
实现 Filter 接口
package cn.knightzz.filter; import javax.servlet.*; import java.io.IOException; public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("LoginFilter init ..."); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException { System.out.println("进入 LoginFilter => doFilter"); chain.doFilter(request, response); System.out.println("离开 LoginFilter => doFilter"); } @Override public void destroy() { System.out.println("LoginFilter destroy ..."); } }
-
在
web.xml
中配置- 这里需要特别注意 : 如果
url-pattern
配置失败,
<filter> <filter-name>LoginFilter</filter-name> <filter-class>cn.knightzz.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/login</url-pattern> </filter-mapping>
- 这里需要特别注意 : 如果
1.4 Filter接口
javax.servlet.Filter接口主要用于描述过滤器对象,可以对资源的请求和资源的响应操作进行筛选
操作。
void init(FilterConfig filterConfig)
实现过滤器的初始化操作void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
执行过滤操作的功能void destroy()
实现过滤器的销毁操作
1.5 FilterConfig接口
javax.servlet.FilterConfifig接口主要用于描述过滤器的配置信息
-
String getFilterName()
获取过滤器的名字 -
String getInitParameter(String name)
获取指定的初始化参数信息 -
Enumeration getInitParameterNames()
获取所有的初始化操作名称 -
ServletContext getServletContext()
获取ServletContext
对象 -
可以在
web.xml
中设置 并通过init
方法的参数FilterConfig
接口获取<filter> <filter-name>HtmlFilter</filter-name> <filter-class>cn.knightzz.filter.HtmlFilter</filter-class> <!-- 设置初始化参数 --> <init-param> <param-name>username</param-name> <param-value>root</param-value> </init-param> </filter> <filter-mapping> <filter-name>HtmlFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
-
HtmlFilter
public class HtmlFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("HtmlFilter init ..."); String filterName = filterConfig.getFilterName(); System.out.println("filterName = " + filterName); String username = filterConfig.getInitParameter("username"); System.out.println("InitParameter = " + username); } // .... }
1.6 多个过滤器
-
需要注意的是 : 当满足多个过滤条件是 过滤器的顺序是根据
web.xml
配置的顺序决定的 -
主要请求路径满足
url-partten
就会被对应的过滤器所拦截, 然后执行过滤器的doFilter
方法 -
以下面的代码为例 :
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("进入 HtmlFilter => doFilter "); chain.doFilter(request, response); System.out.println("离开 HtmlFilter => doFilter "); }
-
上图的访问顺序如下
- 进入过滤器1, 执行
doFilter
里面chain.doFilter(request, response);
之前的代码 - 执行
chain.doFilter(request, response);
放行到下一个过滤器2 - 进入过滤器2, 执行
doFilter
里面chain.doFilter(request, response);
之前的代码 - 执行
chain.doFilter(request, response);
放行到下一个过滤器3 - 进入过滤器3, 执行
doFilter
里面chain.doFilter(request, response);
之前的代码 - 执行
chain.doFilter(request, response);
放行到服务器 - 服务器执行
doPost
或者doGet
方法 - 进入过滤器3, 执行
doFilter
里面chain.doFilter(request, response);
之后的代码 - 进入过滤器2, 执行
doFilter
里面chain.doFilter(request, response);
之后的代码 - 进入过滤器1, 执行
doFilter
里面chain.doFilter(request, response);
之后的代码 - 返回到浏览器响应
- 进入过滤器1, 执行
1.7 过滤器的特点
- 实现代码的“可插拔性”,即增加或减少某个功能模块,不会影响程序的正常执行。
- 可以将多个相同处理逻辑的模块集中写在过滤器里面,可实现重复利用、也方便代码的维护。
2. Listern原理
2.1 监听器基本概念
- Servlet规范中定义的一种特殊的组件,用来监听Servlet容器产生的事件并进行相应的处理。
- 容器产生的事件分类如下:
- 生命周期相关的事件。
- 属性状态相关的事件。
- 存值状态相关的事件。
- 底层原理是采用接口回调的方式实现
2.2 监听器基本类型
2.3 ServletRequestListener监听器
2.3.1 基本概念
- 在
ServletRequest
创建和关闭时都会通知ServletRequestListener
监听器。
2.3.2 常用方法
void requestInitialized(ServletRequestEvent sre)
实现ServletRequest对象的初始化void requestDestroyed(ServletRequestEvent sre)
实现ServletRequest对象的销毁
2.3.3 代码示例
-
MyServletRequestListener
package cn.knightzz.listener; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; public class MyServletRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { // 监听ServletRequest的销毁 System.out.println("监听ServletRequest的销毁...."); } @Override public void requestInitialized(ServletRequestEvent sre) { // 监听Servlet的初始化 System.out.println("监听ServletRequest的初始化...."); } }
-
web.xml
配置<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <listener> <listener-class>cn.knightzz.listener.MyServletRequestListener</listener-class> </listener> </web-app>
-
MyServlet
package cn.knightzz.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; /** * @author 王天赐 * @title: HelloServlet * @projectName spring-aop-01 * @description: * @website http://knightzz.cn/ * @github https://github.com/knightzz1998 * @date 2022/1/12 21:52 */ @WebServlet(name = "MyServlet", urlPatterns = "/my-servlet") public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("MyServlet init ..."); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet doPost ..."); } @Override public void destroy() { // Tomcat 服务器关闭时, Servlet对象才会销毁 System.out.println("MyServlet destroy ..."); } }
2.3.4 执行结果
监听ServletRequest的初始化....
MyServlet init ...
MyServlet doPost ...
监听ServletRequest的销毁....
2.4 ServletRequestAttributeListener
2.4.1 基本概念
- 向
ServletRequest
添加、删除或者替换一个属性的时候,将会通知ServletRequestAttributeListener
监听器。
2.4.2 常用方法
void attributeAdded(ServletRequestAttributeEvent srae)
增加属性时触发void attributeReplaced(ServletRequestAttributeEvent srae)
修改属性时触发void attributeRemoved(ServletRequestAttributeEvent srae)
删除属性时触发
2.4.3 代码示例
-
MyServletRequestAttributeListener
package cn.knightzz.listener; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; public class MyServletRequestAttributeListener implements ServletRequestAttributeListener { @Override public void attributeAdded(ServletRequestAttributeEvent srae) { String name = srae.getName(); System.out.println("监听 " + name + " 属性的增加..."); } @Override public void attributeRemoved(ServletRequestAttributeEvent srae) { String name = srae.getName(); System.out.println("监听 " + name + " 属性的移除..."); } @Override public void attributeReplaced(ServletRequestAttributeEvent srae) { String name = srae.getName(); System.out.println("监听 " + name + " 属性的替换..."); } }
-
在
web.xml
中配置<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <listener> <listener-class>cn.knightzz.listener.MyServletRequestListener</listener-class> </listener> <listener> <listener-class>cn.knightzz.listener.MyServletRequestAttributeListener</listener-class> </listener> </web-app>
2.4.4 执行结果
监听ServletRequest的初始化....
MyServlet init ...
监听 org.apache.catalina.ASYNC_SUPPORTED 属性的替换...
MyServlet doPost ...
监听 username 属性的增加...
监听 username 属性的替换...
监听 username 属性的移除...
监听ServletRequest的销毁....
2.5 HttpSessionListener
2.5.1 基本概念
- 当一个
HttpSession
刚被创建或者失效(invalidate)的时候,将会通知HttpSessionListener
监听
器。
2.5.2 常用方法
void sessionCreated(HttpSessionEvent se)
当一个HttpSession
对象被创建时会调用这个方法void sessionDestroyed(HttpSessionEvent se)
当一个HttpSession
超时或者调用HttpSession的invalidate()
方法让它销毁时,将会调用这个方法
2.5.3 代码示例
-
MyHttpSessionListener
package cn.knightzz.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("创建 Session 监听 ..."); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("销毁 Session 监听 ..."); } }
-
MyServlet
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet doPost ..."); req.setAttribute("username", "root"); req.setAttribute("username", "2068"); req.removeAttribute("username"); HttpSession session = req.getSession(true); System.out.println("创建 Session ... "); }
-
web.xml
<listener> <listener-class>cn.knightzz.listener.MyHttpSessionListener</listener-class> </listener>
2.5.4 执行结果
监听 username 属性的增加...
监听 username 属性的替换...
监听 username 属性的移除...
创建 Session 监听 ...
创建 Session ...
监听ServletRequest的销毁....
2.6 HttpSessionAttributeListener
2.6.1 基本概念
- HttpSession中添加、删除或者替换一个属性的时候,将会通知HttpSessionAttributeListener监听器
2.6.2 常用方法
2.6.3 代码示例
-
MyHttpSessionAttributeListener
package cn.knightzz.listener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; /** * @author 王天赐 * @title: MyHttpSessionAttributeListener * @projectName spring-aop-01 * @description: * @website http://knightzz.cn/ * @github https://github.com/knightzz1998 * @date 2022/1/13 20:45 */ public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener { @Override public void attributeAdded(HttpSessionBindingEvent event) { System.out.println("监听 Session 添加 Attribute"); } @Override public void attributeRemoved(HttpSessionBindingEvent event) { System.out.println("监听 Session 移除 Attribute"); } @Override public void attributeReplaced(HttpSessionBindingEvent event) { System.out.println("监听 Session 添加 Attribute"); } }
-
web.xml
<listener>
<listener-class>cn.knightzz.listener.MyHttpSessionAttributeListener</listener-class>
</listener>
2.6.4 执行结果
监听 username 属性的增加...
监听 username 属性的替换...
监听 username 属性的移除...
创建 Session 监听 ...
创建 Session ...
监听 Session 添加 Attribute
监听 Session 添加 Attribute
监听 Session 移除 Attribute
监听ServletRequest的销毁....
2.7 ServletContextListener
2.7.1 基本概念
- 在ServletContext创建和关闭时都会通知ServletContextListener监听器。
2.7.2 常用方法
void contextInitialized(ServletContextEvent sce)
当ServletContext创建的时候,将会调用这个方法void contextDestroyed(ServletContextEvent sce)
当ServletContext销毁的时候(例如关闭应用服务器 或者重新加载应用),将会调用这个方法
2.7.3 代码示例
package cn.knightzz.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Servlet Context 初始化 ... ");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Servlet Context 销毁 ... ");
}
}
2.7.4 执行结果
- Tomact服务器在启动的时候会自动创建 ServletContext 对象, 在关闭的时候销毁
2.8 ServletContextAttributeListener
2.8.1 基本概念
- 向ServletContext添加、删除或者替换一个属性的时候,将会通知
ServletContextAttributesListener
监听器
2.8.2 常用方法
void attributeAdded(ServletContextAttributeEvent scae)
往ServletContext中加入一个属性的时候触发void attributeRemoved(ServletContextAttributeEvent scae)
从ServletContext中删除一个属性的时候触发void attributeReplaced(ServletContextAttributeEvent scae)
改变ServletContext中属性的时候触发
2.8.3 代码示例
-
MyServletContextAttributeListener
package cn.knightzz.listener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; public class MyServletContextAttributeListener implements ServletContextAttributeListener { @Override public void attributeAdded(ServletContextAttributeEvent event) { System.out.println("监听 Servlet Context 属性添加"); } @Override public void attributeRemoved(ServletContextAttributeEvent event) { System.out.println("监听 Servlet Context 属性移除"); } @Override public void attributeReplaced(ServletContextAttributeEvent event) { System.out.println("监听 Servlet Context 属性替换"); } }
-
MyServlet
package cn.knightzz.servlet; import javax.servlet.ServletContext; 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 javax.servlet.http.HttpSession; import java.io.IOException; /** * @author 王天赐 * @title: HelloServlet * @projectName spring-aop-01 * @description: * @website http://knightzz.cn/ * @github https://github.com/knightzz1998 * @date 2022/1/12 21:52 */ @WebServlet(name = "MyServlet", urlPatterns = "/my-servlet") public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("MyServlet init ..."); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet doPost ..."); req.setAttribute("username", "root"); req.setAttribute("username", "2068"); req.removeAttribute("username"); HttpSession session = req.getSession(true); System.out.println("创建 Session ... "); session.setAttribute("name", "TOM"); session.setAttribute("name", "TOM"); session.removeAttribute("name"); // 获取ServletContext ServletContext servletContext = this.getServletContext(); servletContext.setAttribute("param", "TSK"); servletContext.setAttribute("param", "2022"); servletContext.removeAttribute("param"); } @Override public void destroy() { // Tomcat 服务器关闭时, Servlet对象才会销毁 System.out.println("MyServlet destroy ..."); } }
-
web.xml
<listener> <listener-class>cn.knightzz.listener.MyServletContextAttributeListener</listener-class> </listener>
2.8.4 执行结果
监听 Servlet Context 属性添加
监听 Servlet Context 属性替换
监听 Servlet Context 属性移除
2.9 HttpSessionBindingListener
2.9.1 基本概念
- HttpSession中绑定对象和解除绑定对象时,将会通知HttpSessionListener监听器。
- 需要继承 HttpSessionBindingListener
2.9.2 常用方法
void valueBound(HttpSessionBindingEvent event)
有对象绑定时调用该方法void valueUnbound(HttpSessionBindingEvent event)
有对象解除绑定时调用该方法
2.9.3 代码示例
-
Person :需要继承
HttpSessionBindingListener
不需要配置web.xml
package cn.knightzz.entity; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class Person implements HttpSessionBindingListener { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("监听 Session 对象 绑定"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("监听 Session 对象 解绑定"); } }
-
MyServlet
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet doPost ..."); req.setAttribute("username", "root"); req.setAttribute("username", "2068"); req.removeAttribute("username"); HttpSession session = req.getSession(true); System.out.println("创建 Session ... "); session.setAttribute("name", "TOM"); session.setAttribute("name", "TOM"); session.removeAttribute("name"); // 获取ServletContext ServletContext servletContext = this.getServletContext(); servletContext.setAttribute("param", "TSK"); servletContext.setAttribute("param", "2022"); servletContext.removeAttribute("param"); // 定义Session绑定属性 Person person = new Person(); person.setAge(18); person.setName("KSF"); session.setAttribute("person", person); }
2.9.4 执行结果
监听 username 属性的增加...
监听 username 属性的替换...
监听 username 属性的移除...
创建 Session 监听 ...
创建 Session ...
监听 Session 添加 Attribute
监听 Session 添加 Attribute
监听 Session 移除 Attribute
监听 Servlet Context 属性添加
监听 Servlet Context 属性替换
监听 Servlet Context 属性移除
监听 Session 对象 绑定
监听 Session 添加 Attribute
监听ServletRequest的销毁....
2.10 HttpSessionActivationListener
2.10.1 基本概念
- 当有session数值的钝化和活化操作时,将会通知
HttpSessionActivationListener
监听器。 - 钝化也叫持久化, 因为Session数据是存储在服务器内存中的, 服务器关闭的时候需要把session保存成文件存储在硬盘上
- 活化就是将数据从硬盘上读取出来到内存的过程
2.10.2 常用方法
void sessionWillPassivate(HttpSessionEvent se)
有钝化操作时调用该方法void sessionDidActivate(HttpSessionEvent se)
有活化操作时调用该方法
2.10.3 代码示例
-
Student 类 需要实现
Serializable, HttpSessionActivationListener
package cn.knightzz.entity; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionEvent; import java.io.Serializable; public class Student implements Serializable, HttpSessionActivationListener { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void sessionWillPassivate(HttpSessionEvent se) { System.out.println(" 将Session信息保存到本地 ... "); } @Override public void sessionDidActivate(HttpSessionEvent se) { System.out.println(" 将Session文件从本地读取到内存中 ... "); } }
-
MyServlet
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet doPost ..."); req.setAttribute("username", "root"); req.setAttribute("username", "2068"); req.removeAttribute("username"); HttpSession session = req.getSession(true); System.out.println("创建 Session ... "); session.setAttribute("name", "TOM"); session.setAttribute("name", "TOM"); session.removeAttribute("name"); // 获取ServletContext ServletContext servletContext = this.getServletContext(); servletContext.setAttribute("param", "TSK"); servletContext.setAttribute("param", "2022"); servletContext.removeAttribute("param"); // 定义Session绑定属性 Person person = new Person(); person.setAge(18); person.setName("KSF"); session.setAttribute("person", person); Student student = new Student(); student.setAge(18); student.setName("KSF"); session.setAttribute("student", student); }
2.10.4 执行结果
MyServlet destroy ...
将Session信息保存到本地 ...
监听 Session 对象 解绑定
监听 Session 移除 Attribute
Servlet Context 销毁 ...
2.10.5 配置文件持久化路径
-
配置
context.xml
, 这个文件存在 Tomact服务器的 conf 目录下, 在文件里添加如下内容即可<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true"> <!-- 配置文件存放的路径信息,可以自由指定 --> <Store className="org.apache.catalina.session.FileStore" directory="C:\session"/> </Manager>
3. 在线用户数量统计
3.1 原理解析
- 每个浏览器访问服务器都会创建一个
Session
对象, 这样Session
的创建和销毁可以表示用户在线和下线 - 而一个服务器只会创建一个
ServletContext
对象 - 当新的
Session
创建的时候, 将当前的在线人数加1, 并存储在ServletContext对象上
3.2 代码案例
-
OnlineUser 需要实现
HttpSessionListener, ServletContextListener
接口package cn.knightzz.entity; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class OnlineUser implements HttpSessionListener, ServletContextListener { ServletContext context = null; @Override public void contextInitialized(ServletContextEvent sce) { this.context = sce.getServletContext(); context.setAttribute("onlineUser", 0); } @Override public void contextDestroyed(ServletContextEvent sce) { this.context = null; } @Override public void sessionCreated(HttpSessionEvent se) { Integer onlineUser = (Integer) context.getAttribute("onlineUser"); context.setAttribute("onlineUser", onlineUser + 1); System.out.println("新增在线人数 1 人, 当前在线人数 : " + (onlineUser + 1)); } @Override public void sessionDestroyed(HttpSessionEvent se) { Integer onlineUser = (Integer) context.getAttribute("onlineUser"); context.setAttribute("onlineUser", onlineUser - 1); System.out.println("下线人数 1 人, 当前在线人数 : " + (onlineUser - 1)); } }
-
需要在
web.xml
中配置<listener> <listener-class>cn.knightzz.entity.OnlineUser</listener-class> </listener>