第16 天 JavaWEB过滤器和监听器技术

it2025-03-21  25

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

 

转载于:https://www.cnblogs.com/beyondcj/p/6270900.html

最新回复(0)