JavaWEB过滤器和监听器技术
过滤器介绍
什么是过滤器
生活中的例子:
滤水器,口罩,杯子上滤网,渔网
生活中的过滤器:留下我们想要的,排除,我们不想要的。
高考: 只有分数够高的同学才能进入理想的大学。有一部分同学被拦截在大学之外。(起到拦截的作用)
传智播客: 一开始大家都是小白,进入传智播客学习,经历了4个月的学习,毕业之后,具有了一定(月薪10000左右)的编码能力。
(对每一个经过的学员,都增强了学员的编码能力,起到了增强的作用)
JavaWeb中的过滤器的概念: 对请求和响应进行拦截或者增强的对象,就是过滤器。
JavaWeb中的过滤器是什么呢?
Filter接口:功能——对请求和响应进行增强,或者进行拦截。

JavaWEB中的过滤器运行图解

Filter的快速入门(重点:必须掌握)
Filter定义以及创建步骤介绍
package cn.itcast.filter;
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;
/**
* @author wjn
* 总结:过滤器书写步骤
* 第一:创建类实现接口——DemoFilter implements Filter
* 第二:过滤任务写在doFilter方法中
* 第三:web.xml中配置
*/
public class  
    @Override
    //销毁的方法
    public void destroy() {
    }
    @Override
    //执行过滤的方法
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        System.out.println("DemoFilter.....doFilter....");
    }
    @Override
    //初始化的方法
    public void init(FilterConfig arg0) throws ServletException {
    }
} 
Filter 是在 Web 应用程序的部署描述符中配置的——过滤器创建好之后,需要在web.xml中做配置
在web.xml文件中配置过滤器
<filter> <filter-name>DemoFilter</filter-name> <filter-class>cn.itcast.filter.DemoFilter</filter-class> </filter> <filter-mapping> <filter-name>DemoFilter</filter-name> <url-pattern>/1.txt</url-pattern> </filter-mapping>Filter拦截操作效果

过滤器放行的对象:FilterChain功能介绍

FilterChain的doFilter方法:

代码实现

过滤器放行执行过程:

Filter生命周期
为什么要学习生命周期?
(servlet,只有知道servlet是在什么时候创建和什么时候销毁,才能知道,我在什么时候可以使用servlet)
我需要知道servlet存活的时间,才能正确的使用servlet对象。
对于过滤器,我们同样要知道,过滤器什么时候被创建,什么时候被销毁,我们才能正确的使用过滤器。
Filter生命周期
回顾servlet的生命周期:
创建: 第一次被访问的时候
销毁: 服务器关闭的时候,或者当前项目从服务器中移除
回顾session的生命周期:
创建: 第一次调用getsession方法
销毁: 服务器非正常关闭,超过生存时间,调用销毁(自杀)的方法
Filter:
创建:在服务器启动的时候
服务器启动截图:

销毁: 在服务器关闭的时候,过滤器销毁。
服务器关闭截图:

FilterConfig介绍
servletConfig对象:获取servlet相关的配置信息。
FilterConfig定义:获取filter相关的配置信息。

API介绍:

API代码演示:
1)设置过滤器初始化参数
2)通过filterconfig对象来获取参数

参数配置:

效果演示:

同学提问:filter是不是单例的?
- 类比servlet,我们通过什么来测试,servlet是单例的?
 
测试单例的思路:
- 设置一个成员变量在过滤器中
 - 发送两次请求,都去操作成员变量
 - 如果前一次请求操作的结果,影响后一次请求获取到的成员变量,那么filter就是单例的,反之,不是单例。
 
Filter配置详解(web.xml中的配置)
关于url-pattern配置
过滤器如何匹配请求的路径?
回顾servlet的url-pattern:
全路径匹配——
地址栏:localhost:8080/项目根路径/资源路径 localhost:8080/itcast-filter2/1.txt
通配符的匹配——
地址栏:localhost:8080/项目根路径/abc/*
以上两种匹配方式,配置路径的时候必须以"/"开头

后缀名匹配——/路径/*.do: *.do *.txt *.action
地址栏:localhost:8080/项目根路径/*.txt
后缀名匹配方式,配置路径的时候不能以"/"开头
Filter的url-pattern配置与servlet一致。
过滤器的执行顺序?
测试方式:
- 两个过滤器,拦截同一个请求
 - 调整两个过滤器的配置,再来看执行的顺序
 
总结:
过滤器执行的顺序是按照,web.xml中filter-mapping标签的书写顺序执行(从上往下执行)
关于servlet-name配置
什么是servlet-name配置?
定义:针对指定的servlet进行拦截或者增强操作的配置
Servlet:
package cn.itcast.web; 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 DemoServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("DemoServlet.....执行......."); } public void doPost(HttpServletRequest request, HttpServletResponse throws ServletException, IOException { doGet(request, response); } }Filter:
package cn.itcast.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; /** * @author wjn * 1:定义一个类,实现javax.servlet.Filter; * 2:要进行拦截或者增强的代码,要写在doFilter方法中 * 3:在web.xml中做配置 */ public class @Override//初始化的方法 public void init(FilterConfig config) throws ServletException { System.out.println("MyFilter....init...."); //获取过滤器名称 //选中要输出的内容,按住alt,点击两次右斜线 System.out.println("FilterName:"+config.getFilterName()); //获取指定的初始化参数 String plane = config.getInitParameter("plane"); System.out.println("plane:"+plane); String train = config.getInitParameter("train"); System.out.println("train:"+train); //获取所有初始化参数的名称 Enumeration<String> enumeration = config.getInitParameterNames(); while(enumeration.hasMoreElements()){ System.out.println(enumeration.nextElement()); } } @Override//执行过滤任务的方法 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter.....doFilter....."); //chain:是放行请求和响应的对象 chain.doFilter(request, response); } @Override//销毁的方法 public void destroy() { System.out.println("MyFilter....destroy...."); } }Web.xml配置:

怎么想公司里的老司机请教:
- 我现在在做一个什么功能。
 - 现在出现了什么状况(报错,页面显示,什么都没有发生)
 - 我预期的效果是什么(我的思路是什么)
 
Filter案例--自动登录(重点:必须掌握)
分析
自动登陆的功能需求?
用户懒,不想输入用户名和密码,希望,访问网站,直接自动登陆
用户的使用场景:
用户点击网站,访问项目根路径的时候,启动自动登陆
实现思路:
- 用户在第一次登陆网站,保存用户名和密码(使用Cookie技术)
 - 第二次访问网站,先访问根路径,启动自动登陆的功能(使用过滤器技术,在请求到达项目根路径之前完成自动登陆)
 
画图分析:


工作的时候,自动登陆功能,设置一定要慎重。
人人网,微博,论坛,贴吧,可以设置自动登录
银行,企业内网,支付系统,安全系统(国家网络应用,交通信号灯等),慎重选择制作自动登陆
自动登陆功能,本身就是不安全。
数据保存在cookie中,都是保存在用户的个人电脑中。
提供一个用户选择自动登录的选项,修改页面

LoginServlet实现
package cn.itcast.web; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.itcast.domain.User; import cn.itcast.service.UserService; import cn.itcast.service.impl.UserServiceImpl; public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); //第一步:接受参数 String username = request.getParameter("username"); String pwd = request.getParameter("pwd"); //第二步:调用service方法登录用户 UserService userService = new UserServiceImpl(); User loginUser = userService.login(username ,pwd); //第三步:接收返回值,根据不同返回值不同处理(User == null != null) if(loginUser == null){ request.setAttribute("msg", "用户名或者密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } //登录成功 //需求:在登录页面显示用户名 /* * 第一步:登录成功之后,先记住用户名,通过cookie技术,通过response对象将cookie发送给浏览器 * * 第二步:在登录页面解析cookie,使用EL表达式的内置对象(cookie),再使用javascript进行解码 * */ //=================================自动登录修改========================= String remember = request.getParameter("remember"); if("on".equals(remember)){ //表示用户需要记住用户名和密码自动登录 Cookie c = new Cookie("username", URLEncoder.encode(loginUser.getName(), "utf-8")); c.setMaxAge(60*60*24*7); c.setPath("/"); response.addCookie(c); Cookie c2 = new Cookie("password",pwd ); c2.setMaxAge(60*60*24*7); c2.setPath("/"); response.addCookie(c2); }else{ //用户不需要自动登录 Cookie c = new Cookie("username",""); c.setMaxAge(0); c.setPath("/"); response.addCookie(c); Cookie c2 = new Cookie("password","" ); c2.setMaxAge(0); c2.setPath("/"); response.addCookie(c2); } //=================================自动登录修改========================= request.getSession().setAttribute("loginUser", loginUser); //response.sendRedirect(request.getContextPath()+"/findAllContact"); //response.sendRedirect(request.getContextPath()+"/queryPage?pageNum=1"); response.sendRedirect(request.getContextPath()+"/queryPage2?pageNum=1"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }过滤器实现
package cn.itcast.filter; import java.io.IOException; import java.net.URLDecoder; 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.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.itcast.domain.User; import cn.itcast.service.UserService; import cn.itcast.service.impl.UserServiceImpl; publicclass AutologinFilter implements Filter{ @Override publicvoid destroy() { } @Override publicvoid doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //第一步: 获取数据 (cookie username password) //HttpServletRequest? HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; Cookie[] cookies = req.getCookies(); if(cookies == null){ res.sendRedirect(req.getContextPath()+"/login.jsp"); return; }else{ //将cookie中的username和password String username = ""; String password = ""; for (Cookie cookie : cookies) { if("username".equals(cookie.getName())){ username = URLDecoder.decode(cookie.getValue(), "utf-8") ; } if("password".equals(cookie.getName())){ password = cookie.getValue(); } } if(username.equals("") || password.equals("")){ res.sendRedirect(req.getContextPath()+"/login.jsp"); return; }else{ //第二步:调用方法(UserService.login()) UserService userService = new UserServiceImpl(); User loginUser = userService.login(username, password); //第三步:根局不同返回值,不同处理 if(loginUser == null){ res.sendRedirect(req.getContextPath()+"/login.jsp"); return; }else{ req.getSession().setAttribute("loginUser", loginUser); res.sendRedirect(req.getContextPath()+"/queryPage2?pageNum=1"); return; } } } } @Override publicvoid init(FilterConfig arg0) throws ServletException { } }web.xml配置:
<!-- =======================过滤器配置=============================== --> <filter> <filter-name>AutologinFilter</filter-name> <filter-class>cn.itcast.filter.AutologinFilter</filter-class> </filter> <filter-mapping> <filter-name>AutologinFilter</filter-name> <!-- 因配置文件中默认的主页index.jsp,所访问根路径的时候,默认会跳转到主页上,所以,我们配置 url-pattern使用index.jsp --> <url-pattern>/index.jsp</url-pattern> </filter-mapping> <!-- =======================过滤器配置=============================== -->案例--解决day14_Contact项目中乱码
需求:请求参数在每一个servlet中单独中文乱码处理,代码重复


优化的思路,使用一个过滤器,在请求到达servlet之前,先对象request对象进行设置编码
要对所有的请求都要进行设置编码,都要拦截,进行增强,url-pattern:/*
过滤器代码:
package cn.itcast.filter; 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; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class EncodingFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 强制转换request response HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; //处理响应乱码 res.setContentType("text/html;charset=utf-8"); //处理POST请求乱码 req.setCharacterEncoding("utf-8"); chain.doFilter(req, res); } @Override public void destroy() { } }Web.xml配置:

补充(装饰(包装)设计模式口诀):
- 定义一个类,实现被装饰对象的接口
 - 定义一个成员变量,记住被装饰对象的引用
 - 定义构造方法,传入被装饰对象的实例
 - 改写要修改的方法
 - 不需要改写的方法,调用被装饰对象的原来的方法
 
补充:什么时候使用装饰设计模式
当我们需要对一个类进行增强的时候,增强后的类不再当前类的范畴(animal类型 cat dog都属于动物类型中可以使用继承,电子狗,不属于动物范围,所以选择使用包装设计模式 )中
复杂过滤器实现:
package cn.itcast.filter; 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; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.itcast.domain.MyRequest; public class EncodingFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 强制转换request response HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; //处理响应乱码 res.setContentType("text/html;charset=utf-8"); //自定义一个request对象:MyRequest,对服务器原来的request进行增强,使用装饰设计模式 //要增强原来的request对象,必须先获取到原来的request对象 MyRequest myrequest = new MyRequest(req); //注意:放行的时候应该,传入增强后的request对象 chain.doFilter(myrequest, res); } @Override public void destroy() { } }自定义增强类:
package cn.itcast.domain; import java.io.UnsupportedEncodingException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; //第一问题:HttpServletRequestWrapper他是什么? //HttpServletRequestWrapper:它实现了HttpServletRequest接口,继承当前对象之后,也是HttpServletRequest接口的实现类 //第一问题:增强request,可以直接实现接口,为什么要继承HttpServletRequestWrapper? //因为HttpServletRequestWrapper,已经实现了接口的方法,我们只需继承就可以使用, //如果需要对某些方法增强,只需要修改部分方法即可,其他,调用父类的方法就完成了 /** * 补充(装饰(包装)设计模式心法): 1) 定义一个类,实现被装饰对象的接口 2) 定义一个成员变量,记住被装饰对象的引用 3) 定义构造方法,传入被装饰对象的实例 4) 改写要修改的方法 5) 不需要改写的方法,调用被装饰对象的原来的方法 * */ //1) 定义一个类,实现被装饰对象的接口 public class MyRequest extends HttpServletRequestWrapper{ //2) 定义一个成员变量,记住被装饰对象的引用 private HttpServletRequest request = null; //3) 定义构造方法,传入被装饰对象的实例 //设置一个标记,用来防止,编码多次运行,要保证get方式编码,只运行一次 private boolean flag = false; public MyRequest(HttpServletRequest request) { super(request); this.request = request; } //4) 改写要修改的方法 //所有获取参数的方法,都需要改写 @Override public Map<String, String[]> getParameterMap() { //先判断请求的方式——每一次请求,只会有一种请求方式post get String method = this.request.getMethod(); if("post".equalsIgnoreCase(method)){ //post请求方式 try { this.request.setCharacterEncoding("utf-8"); return this.request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return super.getParameterMap(); } }else if("get".equalsIgnoreCase(method)){ //先获取所有的数 Map<String, String[]> map = this.request.getParameterMap(); if(map == null){ return super.getParameterMap(); } //如果flag是false,说明没有执行过,执行中文乱码处理 //如果flag是true,说明执行过乱码处理,不再重复 if(flag){ return map; } //遍历循环map集合,将每一个数据进行中文乱码处理 //循环map集合的时候,先获取所有key的Set集合,然后,根据key,获取value值 //当前循环结束,map集合中所有数据处理完成 for (String key : map.keySet()) { //获取的数据是String数组 String[] value = map.get(key); //当前for循环结束之后,value中的数据全部处理完成 for(int i = 0 ;i< value.length ;i++){ try { String temp = new String(value[i].getBytes("iso-8859-1"),"utf-8"); //再存入原来的位置 value[i] = temp; } catch (UnsupportedEncodingException e) { e.printStackTrace(); //这里还在继续循环,所以不能return结束 } } } //循环结束,标记设置为true flag = true; return map; }else{ return super.getParameterMap(); } } @Override public String[] getParameterValues(String name) { //先获取所有的数据,map Map<String, String[]> map = this.getParameterMap(); if(map == null){ return super.getParameterValues(name); } //获取map集合中指定数据,根据name指定,相当于key String[] values = map.get(name); return values; } @Override public String getParameter(String name) { //获取指定请求参数的数组 String[] values = this.getParameterValues(name); if(values == null){ return super.getParameter(name); } //如果有数据,返回,数组中的第一个数据 return values[0]; } }注意:最后还有去掉原来设置编码的代码。
监听器介绍
什么是监听器
生活中的例子:
银行的自动门,班导
监听器:监听事件源,根据事件源上发生事件,做出相应的处理。

监听机制相关概念
事件源:发生事件的源头,监听器需要监听的对象。
事件:事件源上发生的动作,监听器监听的内容。
监听器:负责监听事件源的对象。

web监听器介绍
javaweb监听器介绍
JavaWEB中的监听器主要监听JavaWEB中的request、session、ServletContext对象的各种变化。
主要监听的任务:
- 监听request、ServletContext 、session对象的创建和销毁 (练习)
- ServletRequestListener、ServletContextListener、HttpSessionListener
 
 - 监听request、session、ServletContext 对象存放的数据变化情况(练习)
- ServletContextAttributeListener 、HttpSessionAttributeListener 、ServletRequestAttributeListener
 
 - 监听session中保存的JavaBean的状态
- HttpSessionBindingListener
 
 
javaweb监听器创建步骤(示例:ServletRequestListener)
需要定义一个类实现对应的监听器接口
ServletRequestListener定义(API截图):


代码演示:
package cn.itcast.listener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; public class @Override //监听request对象销毁的方法 public void requestDestroyed(ServletRequestEvent sre) { System.out.println("MyServletRequestListener.....requestDestroyed...."); } @Override //监听request对象初始化的方法 public void requestInitialized(ServletRequestEvent sre) { System.out.println("MyServletRequestListener.....requestInitialized...."); } }
配置监听器对象

注意:当服务器加载项目的时候,会读取web.xml文件中listener标签,那么服务器会自动创建监听器对象,并且自动调用其方法
监听器的小结:
- 创建一个类,实现监听器接口
 - 在监听器对象的方法中,书写相关的代码
 - 在web.xml中配置当前监听器。
 
ServletContext创建销毁监听(ServletContextListener)
ServletContextListener定义(API截图):


代码演示:
package cn.itcast.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("MyServletContextListener.....contextInitialized...."); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("MyServletContextListener.....contextDestroyed...."); } }监听器配置:
<listener> <listener-class>cn.itcast.listener.MyServletContextListener</listener-class> </listener>监听servletcontext对象初始化截图:

监听servletcontext对象销毁截图:

案例:定时任务演示
需求:项目启动时,获取服务器时间(new Date()),每一秒钟更新一次,打印在控制台
思路:
1)监控项目的启动(使用ServletContextListener来监听ServletContext对象的初始化)
- 获取服务器时间:new Date();
 - 每一秒更新一次:定时器Timer
 
4)给定时器设置定时任务
Timer:定时器



timeTask:定时器的任务(类)
firstTime:从什么时候开始执行,立即执行设置为:0
period :间隔多少时间重复执行,毫秒值,1秒=1000毫秒
TimerTask:定时器的任务(类)


Run方法中应该写我们的定时任务:每一秒钟更新一次时间,打印在控制台上
代码实现:
package cn.itcast.listener; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * @author wjn * 1) 创建一个类,实现监听器接口 2) 在监听器对象的方法中,书写相关的代码 3) 在web.xml中配置当前监听器。 */ public class MyServletContextListener implements ServletContextListener{ @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("MyServletContextListener....contextInitialized..."); //监控项目的启动(使用ServletContextListener来监听ServletContext对象的初始化) //2) 获取服务器时间:new Date(); //3) 每一秒更新一次:定时器Timer //4) 给定时器设置定时任务 //获取定时器 Timer timer = new Timer(); //调用定时器的设置定时任务的方法 //firstTime 0:立即执行 //period:间隔多长时间执行一次,1000 timer.schedule(new TimerTask() { @Override public void run() { //在run方法中,书写,要执行的任务 //过时的方法一般不推荐使用,但是,过时的方法,jdk不会删除它的效果。 //当前显示时间,可以使用服务器中的时间——java代码,new Date(); //当前显示时间——javascript代码,new Date(); //javascript代码,是在浏览器运行,客户端的时间,一般是不使用客户端的时间 //业务:整点秒杀 //获取的是服务器时间,用户,是没有办法控制 //获取客户端时间,时间有客户控制,时间是不对的 //一般尊循的原则,只要可以控制在服务器的,绝对不给客户端 System.out.println(new Date().toLocaleString()); } }, 0, 1000); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("MyServletContextListener....contextDestroyed..."); } }效果:

HttpSessionListener对象监听session的创建与销毁监听
HttpSessionListener定义(API截图):


代码演示:
package cn.itcast.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("MyHttpSessionListener....sessionCreated...."); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("MyHttpSessionListener....sessionDestroyed...."); } }配置文件:
<listener> <listener-class>cn.itcast.listener.MyHttpSessionListener</listener-class> </listener>Invalidate.jsp页面代码:
<% session.setAttribute("user", new User()); session.removeAttribute("user"); %>
效果截图:

统计在线人数
用户积累:优惠,折扣,广告,扫码关注,想所有QQ用推送一条消息,给所有支付宝用户发送消息。
第三方登录,QQ账号,微博账号,微信账号,优酷账号,支付宝账号,银行账户,百度账号
- 用户体验非常好
 - 创业公司,除了积累用户以外,还获取了用户的QQ或者支付宝,或者微信,可以使用现成推广渠道,再次推广自己应用
 
需求:统计当前访问网站的人数有多少人?
什么时候我们可以知道用户访问了网站?
只要用户访问了我们的网站,session一定会创建。只要用户离开,点退出,session就销毁。
思路:
只要判断session创建,在线人数就加一
只要判断session销毁,在线人数就减一
在线人数的数据,要存在哪里?
ServletContext对象中,所有应用程序范围都可以获取,所有访问当前网站的用户,都应该可以看到在线人数
总思路:
1)先在servletContext中初始化在线人数参数;当前项目初始化的时候,将在线人数初始化:0人。
2)在监听器中只要判断session创建,在线人数就加一
3)在监听器中只要判断session销毁,在线人数就减一
代码实现:

监听器代码:
package cn.itcast.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("MyHttpSessionListener....sessionCreated...."); // 在监听器中只要判断session创建,在线人数就加一 ServletContext context = se.getSession().getServletContext(); // 获取里面的在线人数 Integer onlineNum = (Integer) context.getAttribute("onlineNum"); onlineNum = onlineNum + 1; context.setAttribute("onlineNum", onlineNum); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("MyHttpSessionListener....sessionDestroyed...."); // 在监听器中只要判断session销毁,在线人数就减去一 ServletContext context = se.getSession().getServletContext(); // 获取里面的在线人数 Integer onlineNum = (Integer) context.getAttribute("onlineNum"); onlineNum = onlineNum - 1; context.setAttribute("onlineNum", onlineNum); } }index.jsp显示在线人数,显示退出链接:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> itcast-filter2项目主页<br> 当前在线人数:${onlineNum } <a href="${pageContext.request.contextPath }/validate.jsp">退出</a> </body> </html>页面效果:

属性变化的监听
属性监听器介绍
主要是监听使用setAttribute、removeAttribute方法。
ServletContextAttributeListener 专门用于监听ServletContext对象中的属性的变化情况
HttpSessionAttributeListener 专门用于监听session对象中的属性的变化情况
ServletRequestAttributeListener 专门用于监听request对象中的属性的变化情况
它们中的的监听 添加 、删除 、 修改的方法名称全部一致:

代码演示:
Jsp:
<% //添加数据 session.setAttribute("addr", 111); //替换数据 session.setAttribute("addr", 222); //删除数据 session.removeAttribute("addr"); %>监听器:
package cn.itcast.listener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; public class @Override public void attributeAdded(HttpSessionBindingEvent se) { System.out.println("MyHttpSessionAttributeListener....attributeAdded..."); } @Override public void attributeRemoved(HttpSessionBindingEvent se) { System.out.println("MyHttpSessionAttributeListener....attributeRemoved..."); } @Override public void attributeReplaced(HttpSessionBindingEvent se) { System.out.println("MyHttpSessionAttributeListener....attributeReplaced..."); } }配置文件:
<listener> <listener-class>cn.itcast.listener.MyHttpSessionAttributeListener</listener-class> </listener>Bean监听演示
Session中的bean监听
当我们给Session中保存一个Java对象的时候,或者把Java对象从Session中移除的时候会触发专门用来监听Session中对象变化的监听器中的方法。拥有这个方法的对象——HttpSessionBindingListener接口
属性监听和bean监听的区别:
属性监听:是对三个容器中的任何属性(包括对象和不是对象的数据,基本类型数据)的变化,进行监听
Bean监听:它只监听javabean对象往session中保存和session中移出的过程。


由于HttpSessionBindingListener是用来监听某个JavaBean对象的绑定和解绑的,所以这个监听器的实现类必须是被操作的JavaBean(HttpSessionBindingListener不需要再web.xml中配置)
javaBean:
package cn.itcast.domain; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class User implements HttpSessionBindingListener{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User [age=" + age + ", name=" + name + "]"; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("User....valueBound..."); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("User....valueUnbound..."); } }JSP:
<%
session.setAttribute("user", new User());
session.removeAttribute("user");
%>
<% session.setAttribute("user", new User()); session.removeAttribute("user"); %>效果:

Bean监听需求:
在线人数,根据session创建和销毁,来做人数的增减。
在线会员统计:
- User类实现bean监听接口
 - 每次监听到loginUser对象被绑定到session中的时候,会员人数加一
 - 每次监听到loginUser对象被解绑的时候,会员人数减一
 
作业:
- 自动登录过滤器(40点积分)
 - 定时任务(20点积分)
 - 统计在线任务(进度20点积分)
 - 全站乱码过滤器简单版(20点积分)
 - 全站乱码过滤器复杂版(50点积分)
 
JavaWEB过滤器和监听器技术的更多相关文章
- 第16 天 JavaWEB过滤器和监听器技术
		
Day16 JavaWEB过滤器和监听器技术 复习: 1.大结果集分页mysql的实现,是使用那个关键字,从user表中取第一页的数据,长度为10,sql语句怎么写? 2.分页查询的起始位置(star ...
 - JavaWeb——过滤器及监听器
		
什么是过滤器? 过滤器示意图 Filter是如何实现拦截的? Filter的生命周期 Filter的创建 Filter的销毁 FilterConfig接口 Servlet过滤器有关接口 过滤器配置 F ...
 - JAVAWEB过滤器、监听器的作用及使用>从零开始学JAVA系列
		
目录 JAVAWEB过滤器.拦截器的作用及使用 过滤器Filter 什么是过滤器 为什么要使用过滤器(过滤器所能解决的问题) 配置一个过滤器完成编码的过滤 编写一个EncodingFilter(名称自 ...
 - struts2 javaweb 过滤器、监听器 拦截器 原理
		
转: 过滤器.监听器 拦截器 过滤器 创建一个 Filter 只需两个步骤: (1)创建 Filter 处理类: (2)在 web.xml 文件中配置 Filter . 创建 Filter 必须实现 ...
 - java-web 过滤器 & 监听器 & 拦截器
		
Tomcat 的容器分为四个等级.真正管理 Servlet 的容器是 Context 容器,一个 Context 对应一个 Web 工程.在 Tomcat 的配置文件里能够非常easy发现这一点.例如 ...
 - 超全面的JavaWeb笔记day20<监听器&国际化>
		
JavaWeb监听器 三大组件: l Servlet l Listener l Filter Listener:监听器 1. 初次相见:AWT 2. 二次相见:SAX 监听器: l 它是一个接口,内容 ...
 - Java基础——过滤器和监听器
		
什么是过滤器? Servlet过滤器和Servlet十分相似,但它具有拦截客户端请求的功能,Servlet过滤器可以改变请求中的内容,来满足实际开发中的需要.对于开发人员而言,过滤器实际上就是在Web ...
 - JavaWeb中的监听器
		
JavaWeb中的监听器 l 事件源:三大域! ServletContext ¨ 生命周期监听:ServletContextListener,它有两个方法,一个在出生时调用,一个在死亡时 ...
 - 面试之servlet、过滤器、监听器
		
servlet.过滤器.监听器servlet是Java中WEB请求和响应的容器servlet的运行需要在类似tomcat容器中,一个 Web 应用对应一个 Context 容器,也就是 Servlet ...
 
随机推荐
- 微信小程序传递URL中含有特殊字符
			
小程序传递URL中含有特殊字符"="时,解决办法:先encodeURIComponent,取到值以后再decodeURIComponent 首先在A页面 var urls = en ...
 - [JZOJ3337] 【NOI2013模拟】wyl8899的TLE
			
题目 题目大意 给你两个字符串\(A\)和\(B\),可以修改\(A\)中的一个字符,求修改后最长的\(A\)的前缀,使它是\(B\)的子串. 思考历程 看到这道题之后,第一眼想到的就是后缀自动机! ...
 - DELPHI实现类似仿360桌面的程序界面
			
1.窗体半透明: Alphablend属性为true;Alphablendvalue的值为100 2.窗体透明: formCreate: Self.TransparentColor := True;S ...
 - 尚学linux课程---9、yum相关操作和知识
			
尚学linux课程---9.yum相关操作和知识 一.总结 一句话总结: 如何使用比如163,阿里云给yum配置yum源:去官网,不要百度:直接去官网,有帮助文档的(比如centos的就在centos ...
 - Docker系列(七):Docker图形化管理和监控
			
Docker管理工具之官方三剑客 Docker Machine是什么鬼 从前 现在 你需要登录主机,按照主机及操作系统特有的安装以及配置步骤安装Docker,使其 能运行Docker容器. Docke ...
 - 关于Ubuntu锁屏后,无法输入密码
			
屏幕锁定后字母密码无法输入,数字可以,切换用户然后可以正常输入密码登陆. 是输入法的事,锁屏界面依然是中文输入法,然后输入字母之后先不会显示然后加上数字后是汉字密码不对. 切换输入法到英文,就可以正常 ...
 - VI/VIM 无法使用系统剪贴板(clipboard)
			
来自: http://www.bubuko.com/infodetail-469867.html vim 系统剪贴板 "+y 复制到系统剪切板 "+p 把系统粘贴板里的内容粘贴到v ...
 - Python Flask高级编程之从0到1开发《鱼书》精品项目 ✍✍✍
			
Python Flask高级编程之从0到1开发<鱼书>精品项目 一 .安装环境我们使用 flask web框架,并用 sqlalchemy来做数据库映射,并使用 migrate做数据迁移 ...
 - Cefsharp实现快捷键功能
			
原文:Cefsharp实现快捷键功能 1 . 实现IKeyboardHandler接口 public class KeyBoardHander : IKeyboardHandler { public ...
 - websocket 实现消息推送(转)
			
介绍 现很多网站为了实现即时通讯,所用的技术都是轮询(polling).轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器. ...