Day16 JavaWEB过滤器和监听器技术
复习:
1、大结果集分页mysql的实现,是使用那个关键字,从user表中取第一页的数据,长度为10,sql语句怎么写?
2、分页查询的起始位置(startIndex)如何计算?
3、分页数据中的尾页如何计算?
4、分页Service的业务处理有那几个步骤?
-
过滤器介绍
什么是过滤器
生活中的例子:
滤水器,口罩,杯子上滤网,渔网
生活中的过滤器:留下我们想要的,排除,我们不想要的。
高考: 只有分数够高的同学才能进入理想的大学。有一部分同学被拦截在大学之外。(起到拦截的作用)
传智播客: 一开始大家都是小白,进入传智播客学习,经历了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 DemoFilter implements Filter{
@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 response)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
MyFilter
implements Filter{@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
MyServletRequestListener
implements ServletRequestListener{@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
MyServletContextListener
implements ServletContextListener{@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
MyHttpSessionListener
implements HttpSessionListener{@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页面代码:
效果截图:
-
统计在线人数
用户积累:优惠,折扣,广告,扫码关注,想所有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
MyHttpSessionAttributeListener
implements HttpSessionAttributeListener {@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");
%>
效果:
Bean监听需求:
在线人数,根据session创建和销毁,来做人数的增减。
在线会员统计:
- User类实现bean监听接口
- 每次监听到loginUser对象被绑定到session中的时候,会员人数加一
- 每次监听到loginUser对象被解绑的时候,会员人数减一
作业:
- 自动登录过滤器(40点积分)
- 定时任务(20点积分)
- 统计在线任务(进度20点积分)
- 全站乱码过滤器简单版(20点积分)
- 全站乱码过滤器复杂版(50点积分)