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点积分)
-