美文网首页Springbootjava学习Spring
九、Listener 监听器& Filter过滤器

九、Listener 监听器& Filter过滤器

作者: 圣贤与无赖 | 来源:发表于2018-10-04 14:28 被阅读48次

    一、 Listener监听器

    Javaweb中的监听器是用于监听web常见对象HttpServletRequest,HttpSession,ServletContext
    作用:
    监听web对象的创建与销毁
    监听web对象的属性变化
    监听session绑定javaBean操作
    监听机制的相关概念
    事件----一件事情
    事件源---产生这件事情的源头
    注册监听---将监听器与事件绑定,当事件产生时,监听器可以知道,并进行处理。
    监听器---对某件事情进行处理监听的一个对象

    Servlet的监听器:

    • 监听ServletContext,HttpSession,ServletRequest
    • 事件源和监听器绑定的过程:通过配置完成.

    Servlet中的监听器:提供了8个监听器.
    一类:监听三个域对象的创建和销毁的监听器.3个
    二类:监听三个域对象的属性变更的监听器.(属性添加,属性移除,属性替换)3个.
    三类:监听HttpSession对象中的JavaBean的状态的改变.(绑定,解除绑定,钝化和活化)2个

    1. 一类监听器:监听三个域对象的创建和销毁的监听器

    1.1 ServletContextListener:监听ServletContext对象的创建和销毁.

    【方法】

    ServletContextListener.png

    【问题】
    ServletContext对象何时创建和销毁:

    • 创建:服务器启动时候,服务器可以为每个WEB应用创建一个单独的ServletContext.
    • 销毁:服务器关闭的时候,或者项目从服务器中移除.

    【入门案例】
    1.编写一个类实现监听器的接口.

    public class MyServletContextListener implements ServletContextListener{
    
        @Override
        /**
         * 监听ServletContext对象的创建的方法:
         * @param sce
         */
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("ServletContext对象被创建了...");
        }
    
        @Override
        /**
         * 监听ServletContext对象的销毁的方法:
         * @param sce
         */
        public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("ServletContext对象被销毁了...");
        }
    
    }
    
    1. 通过配置完成监听器和事件源的绑定.
     <!-- 配置监听器 -->
      <listener>
        <listener-class>com.itheima.weblistener.MyServletContextListener</listener-class>
      </listener>
    

    【企业中应用】

    • 初始化工作.
    • 加载配置文件:Spring框架.
      ContextLoaderListener:
    • 定时任务调度:
      Timer,TimerTask.
    1.2 HttpSessionListener:监听HttpSession对象的创建和销毁的监听器.

    【方法】

    HttpSessionListener.png

    【问题】

    • HttpSession对象何时创建和销毁的?
      • 创建:服务器第一次调用getSession()方法的时候.
      • 销毁:
        • 非正常关闭服务器(正常关闭序列化到硬盘)
        • session过期了(默认30分钟)
        • session.invalidate()

    【入门】

    1. 编写监听器:
    public class MyHttpSessionListener implements HttpSessionListener {
    
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            System.out.println("HttpSession对象被创建了...");
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            System.out.println("HttpSession对象被销毁了...");
        }
    
    }
    
    
    1. 配置监听器:
    <listener>
        <listener-class>com.itheima.weblistener.MyHttpSessionListener</listener-class>
     </listener>
    

    【问题】
    1.访问html是否创建session对象? 不会
    2.访问一个Servlet是否创建session对象? 不会
    3.访问一个jsp是否创建session对象? 会

    1.3 ServletRequestListener:监听ServletRequest对象的创建和销毁的监听器

    【方法】

    ServletRequestListener.png

    【问题】
    ServletRequest对象何时创建和销毁?

    • 创建:客户端向服务器发送请求的时候.
    • 销毁:服务器为这次请求作出了响应时候.

    【入门】
    1.编写一个监听器

    public class MyServletRequestListener implements ServletRequestListener {
        public void requestInitialized(ServletRequestEvent sre)  { 
            System.out.println("ServletRequest被创建了...");
        }
        public void requestDestroyed(ServletRequestEvent sre)  { 
            System.out.println("ServletRequest被销毁了...");
        }
        
    }
    
    1. 配置监听器
     <listener>
        <listener-class>com.itheima.weblistener.MyServletRequestListener</listener-class>
      </listener>
    

    【问题】
    1.访问html是否创建request对象? 会
    2.访问一个Servlet是否创建request对象? 会
    3.访问一个jsp是否创建request对象? 会

    2. 二类:监听三个域对象属性变更的监听器

    2.1 ServletContextAttributeListener:监听ServletContext对象中的属性变更的监听器
    ServletContextAttributeListener.png
    2.2 HttpSessionAttributeListener:监听HttpSession对象中的属性变更的监听器
    HttpSessionAttributeListener:监听HttpSession对象中的属性变更的监听器.png
    2.3 ServletRequestAttributeListener:监听ServletRequest对象中的属性变更的监听器
    ServletRequestAttributeListener.png

    3. 三类:监听HttpSession中的JavaBean的状态改变的监听器.(绑定,解决绑定,钝化,活化)

    三类监听器非常特殊
    监听器作用在JavaBean上.JavaBean可以自己感知在session中状态.
    这类监听器不用配置.

    3.1 HttpSessionBindingListener:监听HttpSession中的JavaBean的绑定和解除绑定的状态.
    HttpSessionBindingListener.png
    3.2 HttpSessionActivationListener:监听HttpSession中的JavaBean的钝化和活化的状态.
    HttpSessionActivationListener.png

    sessionDidActivate(HttpSessionEvent se); -- 活化
    SessionWillPassivate(HttpSessionEvent se); -- 钝化

    通过配置序列化session:

    context.xml

    • tomcat/conf/context.xml: 对tomcat中的所有虚拟主机和虚拟路径生效.
    • tomcat/conf/Catalina/localhost/context.xml: 对tomcat下的localhost虚拟主机中的所有路径生效.
    • 工程的META-INF/context.xml: 对当前的工程生效.
    <?xml version="1.0" encoding="UTF-8"?>
    <!--
        maxIdleSwap :1分钟 如果session不使用就会序列化到硬盘.
        directory   :itheima 序列化到硬盘的文件存放的位置.
     -->
    <Context>
        <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
            <Store className="org.apache.catalina.session.FileStore" directory="itheima"/>
        </Manager>
    </Context>
    
    

    4. 监听器的总结:

    Servlet的监听器分成三类8个:

    • 一类:监听三个域对象的创建和销毁的监听器.
    • 二类:监听三个域对象的属性的变更.
    • 三类:监听HttpSession中JavaBean的状态的改变

    5. 案例-定时删除过时订单分析

    功能描述:
    若一个订单从下单开始超过30分钟未支付,则删除该订单。
    分析:
    为了这个操作我们需要拿到订单的下单时间和支付状态.然后判断订单是否超过30分钟未支付,若未支付则取消该订单,想实现此功能,还需要使用任务调度功能.要求在项目一启动的时候就可以扫描订单.比如每分钟查找一下,将满足条件的删除掉.

    在java中有一个Timer定时器类 Timer.png

    步骤分析:
    创建一个ServletContext创建与销毁监听器,在ServletContext对象创建时,启动定时扫描器.
    在定时器内部实现查询订单及删除订单操作

    public void contextInitialized(ServletContextEvent sce) {
            //一旦服务器启动 该定时器就开始扫描
            Timer t=new Timer();
            t.schedule(new TimerTask() {
                @Override
                public void run() {
                    //完成订单查询和删除操作即可
                    System.out.println(".........");
                }
            }, 1000, 2000);//延迟1秒开始执行,每2秒执行一次
        }
    

    二、Filter过滤器

    1. filter介绍及其作用介绍

    Filter是sun公司中servlet2.3后增加的一个新功能.
    Servlet规范中三个技术 Servlet Listener Filter
    在javaEE中定义了一个接口 javax.servlet.Filter来描述过滤器
    作用:
    通过Filter可以拦截访问web资源的请求与响应操作.
    WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

    常用api:

    常用api.png

    filter入门案例
    创建步骤:

    1. 编写filter
      a. 创建一个类实现javax.servlet.Filter接口
      b. 重写接口方法
    2. 编写配置文件
      a. 注册filter
      b. 绑定路径

    Filter在web.xml文件中配置的目的:配置拦截什么样的资源。

    Filter初始化

    <filter>
        <filter-name>demo1Filter</filter-name>
        <filter-class>cn.itcast.web.filter.Demo1Filter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>demo1Filter</filter-name>
        <url-pattern>/demo1</url-pattern>
    </filter-mapping>
    
    

    拦截分析:

    拦截分析.png

    注意:
    在Filter的doFilter方法内如果没有执行,那么资源是不会被访问到的。

    FilterChain功能介绍
    FilterChain 是 servlet 容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用 FilterChain 调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。

    FilterChain.png

    2. filter链与生命周期

    filter链介绍
    多个Filter对同一个资源进行了拦截,那么当我们在开始的Filter中执行 chain.doFilter(request,response)时,是访问下一下Filter,直到最后一个Filter执行时,它后面没有了Filter,才会访问web资源。

    如果有多个Filter形成了Filter链,那么它们的执行顺序是怎样确定的?
    它们的执行顺序取决于<filter-mapping>在web.xml文件中配置的先后顺序。

    filter生命周期
    当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
    当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
    当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.

    4. FilterConfig介绍

    Filter功能介绍
    在Filter中的init方法上有一个参数叫FilterConfig,是Filter的配置对象
    作用:
    获取初始化参数
    获取filter的名称
    获取全局管理者(SerlvetContext对象)

    常用api

    FilterConfig.png

    5. filter配置详解

    Filter基本配置介绍

    <filter>
        <filter-name>filter名称</filter-name>
        <filter-class>filter类全名</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filter名称</filter-name>
        <url-pattern>映射路径</url-pattern>
    </filter-mapping>
    

    url-pattern配置
    完全匹配 :
    要求必须以"/"开始.

    目录匹配:
    要求必须以"/"开始,以*结束.

    扩展名匹配:
    不能以"/"开始,以.xxx结束.例如:.jsp *.do

    关于servlet-name配置
    针对于servlet拦截的配置 <servlet-name>配置
    在Filter中它的url-pattern配置项上有一个标签
    <servlet-name>它用于设置当前Filter拦截哪一个servlet。
    是通过servlet的name来确定的。

    关于dispatcher配置
    可以取的值有 REQUEST FORWARD ERROR INCLUDE
    作用:
    当以什么方式去访问web资源时,进行拦截操作.

    REQUEST
    当是从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的 它也是默认值
    FORWARD
    它描述的是请求转发的拦截方式配置
    ERROR
    如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
    INCLUDE
    如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。

    7.Filter案例

    7.1自动登录功能

    之前的登录流程:
    login.jsp-->loginServlet-->UserService-->UserDao
    现在分析下自动登录的原理
    a.当用户登录成功之后,判断一下用户是否勾选了自动登录,若勾选了,将用户名和密码通过cookie持久化到浏览器上.
    b.做一个filter,filter的作用为:判断cookie中是否有用户名和密码,若有拿过来调用service完成登录操作
    c.filter放行

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //1.强转
            HttpServletRequest req=(HttpServletRequest) request;
            HttpServletResponse resp=(HttpServletResponse) response;
            
            //2.逻辑
            //首先判断session中是否有user  若没有再继续操作
            User user = (User) req.getSession().getAttribute("user");
            
            if(user==null){//session中没有用户 需要继续操作
                //获取请求路径 若是login和regist的不需要自动登录
                String uri = req.getRequestURI();
                String contextPath = req.getContextPath();
                String path=uri.substring(contextPath.length());
                //System.out.println(path);
                if(!("/login.jsp".equals(path)||"/login".equals(path)||"/regist.jsp".equals(path)||"/regist".equals(path))){
                    //查找是否有自动登录的cookie
                    Cookie c = CookieUtils.getCookieByName("autoLogin", req.getCookies());
                    if(c!=null){
                        
                        //若有 调用service 自动登录
                        //拿到用户名和密码
                        String username=c.getValue().split("-")[0];
                        String password=c.getValue().split("-")[1];
                        
                        //调用service登录
                        user = new UserService().login(username, password);
                        //若登录成功 将user放入session中
                        if(user!=null){
                            req.getSession().setAttribute("user", user);
                            System.out.println("自动登录..........");
                        }
                    }
                }
                
            }
            
            //3.放行
            chain.doFilter(req, resp);
        }
    
    

    注意:
    cookie中不能存放中文,若出现中文还需编码解决

    7.2编码过滤器

    思路:在Filter中对request进行功能增强,让它处理了乱码问题,再将request传递到servlet中,这样在servlet中获取请求参数就不会乱码。
    如何进行功能加强?

    • 三种方式:
      继承
      装饰者模式
      动态代理

    装饰模式实现步骤:
    a.装饰类与被装饰类要继承同一个父类或实现同一个接口。
    b.在装饰类中重写方法,进行功能增强
    c.在装饰类中持有一个被装饰类的对象

    编码过滤器分析.png

    在EncodingFilter中已经创建了一个MyReqeust,它是一个HttpServletRequest的装饰类,而我们在chain.doFilter(MyRequest,response);也就是在servlet中使用的request其实是装饰类。

    对于我们通过reqeust对象获取请求参数有三种方式:
    getParameter getParameterValues getParameterMap

    我们不需要将这三个都进行编码处理,只需要对getParameterMap进行乱码处理,而getParameter及getParameteValues可以依赖于getParametreMap的实现

    class MyRequest extends HttpServletRequestWrapper{
        private HttpServletRequest request;
        private boolean flag=true;
        
        
        public MyRequest(HttpServletRequest request) {
            super(request);
            this.request=request;
        }
        
        @Override
        public String getParameter(String name) {  
            if(name==null || name.trim().length()==0){
                return null;
            }
            String[] values = getParameterValues(name);
            if(values==null || values.length==0){
                return null;
            }
            
            return values[0];
        }
        
        @Override
        /**
         * hobby=[eat,drink]
         */
        public String[] getParameterValues(String name) {
            if(name==null || name.trim().length()==0){
                return null;
            }
            Map<String, String[]> map = getParameterMap();
            if(map==null || map.size()==0){
                return null;
            }
            
            return map.get(name);
        }
        
        @Override
        /**
         * map{ username=[tom],password=[123],hobby=[eat,drink]}
         */
        public Map<String,String[]> getParameterMap() {  
            
            /**
             * 首先判断请求方式
             * 若为post  request.setchar...(utf-8)
             * 若为get 将map中的值遍历编码就可以了
             */
            String method = request.getMethod();
            if("post".equalsIgnoreCase(method)){
                try {
                    request.setCharacterEncoding("utf-8");
                    return request.getParameterMap();
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }else if("get".equalsIgnoreCase(method)){
                Map<String,String[]> map = request.getParameterMap();
                if(flag){
                    for (String key:map.keySet()) {
                        String[] arr = map.get(key);
                        //继续遍历数组
                        for(int i=0;i<arr.length;i++){
                            //编码
                            try {
                                arr[i]=new String(arr[i].getBytes("iso8859-1"),"utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    flag=false;
                }
                //需要遍历map 修改value的每一个数据的编码
                
                return map;
            }
            
            return super.getParameterMap();
        }
        
    }
    
    

    相关文章

      网友评论

        本文标题:九、Listener 监听器& Filter过滤器

        本文链接:https://www.haomeiwen.com/subject/kkvrgftx.html