提示:这两个东西听起来似乎很难,实际上是非常简单的,按照要求写就行了,一定不要被新名词给吓到了。
JavaWeb中的Filter(过滤器)
- 一、Filter(过滤器)
- 1.如何编写 Filter
- 2.Filter 中的细节
- 3.Filter 总结
- 二、Listener(监听器)
- 1.Servlet规范中提供的监听器
- 2..如何实现 Listener
- 3.Listener 应用场景
一、Filter(过滤器)
- Filter 可以在 Servlet 这个目标程序执行之前添加代码。也可以在目标 Servlet 执行之后添加代码。之前之后都可以添加过滤规则。
- 一般情况都是在过滤器中编写公共代码。
1.如何编写 Filter
-
第一步:编写一个 Java 类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中的方法。
- init方法:在Filter对象第一次被创建之后调用,并且只调用一次。(默认方法,可以不实现)
- doFilter方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。(必须实现这个方法)
- 注意: chain.doFilter(request, response);这个代码是必须要写的,这行代码的作用是执行下一个过滤器或者执行目标Servlet的。如果不写那么就无法执行目标Servlet。
- 在chain.doFilter(request, response); 前写的代码是在目标Servlet执行前执行的,在chain.doFilter(request, response); 后写的代码是在目标Servlet执行后执行的。
- destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。 (默认方法,可以不实现)
public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //获取请求路径 String servletPath = request.getServletPath(); if(true){ //在目标Servlet执行前执行的代码 System.out.println("Servlet执行前......") filterChain.doFilter(request,response);//一定不能少的代码 //在目标Servlet执行后执行的代码 System.out.println("Servlet执行后......") } else { response.sendRedirect(request.getContextPath()); //返回网站的首页 } } }
-
第二步:在web.xml文件中对Filter进行配置。这个配置和Servlet很像。或者使用注解:@WebFilter({“*.do”})
<filter> <filter-name>filter2</filter-name> <filter-class>com.gdb.javaweb.servlet.Filter2</filter-class> </filter> <filter-mapping> <filter-name>filter2</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
2.Filter 中的细节
- Filter的生命周期
- 和Servlet对象生命周期一致。唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。
- Servlet是单例的。Filter也是单例的。(单实例。)
- 关于Filter的优先级
- ① 注意:Filter的优先级,天生的就比Servlet优先级高。
- 例如:/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。
- ② 在web.xml文件中进行配置的时候,Filter的执行顺序:
- 依靠filter-mapping标签的配置位置,越靠上优先级越高。过滤器的调用顺序,遵循栈数据结构。
- ③ 使用@WebFilter的时候,Filter的执行顺序
- 按照类名的字典序的顺序执行。
- ④ 总结:在程序编译阶段不会确定Filter调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。 这种设计模式被称为责任链设计模式。
- ① 注意:Filter的优先级,天生的就比Servlet优先级高。
- 关于Filter的配置路径:
- /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
- /* 匹配所有路径。
- *.do 后缀匹配。不要以 / 开始
- /dept/* 前缀匹配。
- 目标Servlet是否执行,取决于两个条件:
- 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
- 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。
- 第二:用户发送的请求路径是否和Servlet的请求路径一致。
- 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
3.Filter 总结
- 过滤器是使用责任链设计模式实现的。
- 责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序。
- 可以使用过滤器来实现验证用户是否登录,防止未登录访问我们服务器中的数据。
二、Listener(监听器)
- 监听器是 Servlet 规范中的一员。就像 Filter 一样。Filter 也是 Servlet 规范中的一员。
- 在 Servlet 中,所有的监听器接口都是以 “Listener” 结尾。
- 注意:所有监听器中的方法都是不需要javaweb程序员调用的,当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。
1.Servlet规范中提供的监听器
- jakarta.servlet包下:
- ServletContextListener
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- jakarta.servlet.http包下:
- HttpSessionListener
- HttpSessionAttributeListener
- 该监听器需要使用@WebListener注解进行标注。
- 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
- HttpSessionBindingListener
- 该监听器不需要使用@WebListener进行标注。
- 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
- 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
- HttpSessionIdListener
- session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
- HttpSessionActivationListener
- 监听session对象的钝化和活化的。
- 钝化:session对象从内存存储到硬盘文件。
- 活化:从硬盘文件把session恢复到内存。
- ServletContextListener、ServletRequestListener、HttpSessionListener 这三个监听器都提供了两个时间点,一个是域对象创建的时候,另一个是域对象销毁的时候。
- ServletContextAttributeListener、ServletRequestAttributeListener、HttpSessionAttributeListener 这三个监听器都提供了三个时间点,一个是往域中添加数据的时候,一个是删除域中数据的时候,另一个是修改域中的数据的时候。
2…如何实现 Listener
- 第一步:编写一个类实现 ServletContextListener 接口(以这个接口为例而已)。并且实现里面的方法。
void contextInitialized(ServletContextEvent event) void contextDestroyed(ServletContextEvent event)
- 第二步:在 web.xml 文件中对 ServletContextListener 进行配置。或者用注解 @WebListener。
<listener> <listener-class>com.bjpowernode.javaweb.listener.MyServletContextListener</listener-class> </listener>
3.Listener 应用场景
- 请编写一个功能,记录该网站实时的在线用户的个数。
- 我们可以通过服务器端有没有分配 session 对象,因为一个 session 代表了一个用户。有一个 session 就代表有一个用户。如果你采用这种逻辑去实现的话,session 有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener 够用了。session 对象只要新建,则 count++,然后将 count 存储到 ServletContext 域当中,在页面展示在线人数即可。
- 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?
- session.setAttribute(“user”, userObj);
- 用户登录的标志是 session 中曾经存储过 User 类型的对象。那么这个时候可以让 User 类型的对象实现 HttpSessionBindingListener 监听器,只要 User 类型对象存储到 session 域中,则 count++,然后将 count++ 存储到 ServletContext 对象中。页面展示在线人数即可。
public class User implements HttpSessionBindingListener { private String username; private String password; //有参构造、无参构造、toString方法、set和get方法 @Override public void valueBound(HttpSessionBindingEvent event) { //用户登陆 //User类型的对象向session中存储 //获取ServletContext ServletContext application = event.getSession().getServletContext(); //获取在线人数 Object onlineCount = application.getAttribute("onlineCount"); if (onlineCount == null){ application.setAttribute("onlineCount",1); }else { int count = (Integer)onlineCount; count++; application.setAttribute("onlineCount",count); } } @Override public void valueUnbound(HttpSessionBindingEvent event) { //退出登陆 //User类型的对象从session域中删除 //获取ServletContext ServletContext application = event.getSession().getServletContext(); //获取在线人数 Integer onlineCount = (Integer) application.getAttribute("onlineCount"); onlineCount--; application.setAttribute("onlineCount",onlineCount); }