首页 > 代码库 > 过滤器 & 装饰者模式

过滤器 & 装饰者模式

一.过滤器概述
    ------------------------------------------------
    1.1.什么是过滤器?
        Servlet技术规范中, 定义了Servlet、Filter、Listener三门技术, 其中Filter也叫做过滤器,通过过滤器技术,开发人员可以实现用户在访问某个资源之前或之后,对访问的请求和响应进行拦截,从而做一些相关的处理。

        过滤器:
            ◇ 所谓的过滤器, 就是拦截用户对资源的访问
            ◇ 一个过滤器可以拦截多个资源, 一个资源也可以配置多个过滤器进行拦截
            ◇ 其实所谓的拦截, 就是将代表请求的request对象和代表响应的response对象拦截下来, 拦截下来后:
                ◇ 控制是否允许访问 -- 用户登陆之后才能查看自己的订单页面
                ◇ 在访问资源之前或之后做一些处理 比如: 全站乱码解决
                ...
         
===================================================  
二.开发过滤器
    ------------------------------------------------
    2.1.开发过滤器的步骤
        Servlet API中提供了一个Filter接口, 开发web应用时, 如果编写一个类实现了这个接口, 则这个类就是一个过滤器
        (1) 写一个类实现Filter接口, 并实现其中的方法
        (2) 在web应用的web.xml中配置过滤器
        
    ------------------------------------------------
    ~~2.2.Filter生命周期:
        当服务器启动时, web应用加载后会立即创建出当前web应用中的所有的Filter对象, 创建出来后, 立即调用init方法进行初始化出操作. 从此以后这个Filter对象一直驻留在内存中为后续所拦截的请求服务, 每次过滤到对资源的访问时, 都会执行doFilter这个方法进行拦截处理, 直到服务器关闭或者web应用移出容器为止, 随着web应用的销毁, 过滤器也跟着销毁, 在销毁之前会调用destroy方法执行善后的处理.
        
    ------------------------------------------------
    2.3.配置过滤器
        <filter> -- 配置一个过滤器
            <filter-name>FilterDemo1</filter-name>
                   -- 过滤器的名字
            <filter-class>cn.tedu.FilterDemo1</filter-class> -- 过滤器处理类的全路径名
        </filter>
        
        <filter-mapping> -- 为指定的过滤器配置要拦截的路径, 一个过滤器可以配置多个<filter-mapping>
            <filter-name>FilterDemo1</filter-name> -- 过滤器的名字
            <servlet-name>ServletDemo1</servlet-name> -- 拦截哪个名字的Servlet, 可以配置多个
            <url-pattern>/servlet/*</url-pattern> -- 要拦截的路径, 路径的写法和Servlet的<url-pattern>写法一致, 可以配置多个
            <dispatcher>REQUEST</dispatcher> -- 配置拦截哪种方式的对资源的访问, 可以取值为REQUEST/FORWARD/INCLUDE/ERROR
                REQUEST:默认,普通请求,最常用
                FORWARD:所拦截的资源是通过请求转发访问的
                INCLUDE:所拦截的资源是通过页面包含访问的
                ERROR:所拦截的资源通过异常机制访问的
        </filter-mapping>
        
    ------------------------------------------------
    2.4.Filter中的方法介绍
        --------------------------------------------
        init(FilterConfig filterConfig)
            FilterConfig -- 代表当前Filter在web.xml中配置信息的对象
                通过这一对象可以获取当前过滤器在web.xml配置的初始化参数
                通过这一对象可以获取代表当前web应用的ServletContext对象
                获取初始化参数:
                    getInitParameter(String name);
                    getInitParameterNames()
                获取ServletContext对象
                    getServletContext();
                    
        --------------------------------------------
        doFilter(request, response, FilterChian filterChian)
              FilterChian -- 过滤器链
                  一个web资源可以被多个过滤器所拦截, 多个过滤器拦截的顺序是按照Filter在web.xml中配置的<filter-mapping>的顺序执行的.这多个过滤器按照拦截的顺序就组成了一个拦截器链, 用FilterChian表示.

                  如果一个过滤器处理完所拦截的请求后, 想要执行后面的拦截器, 则可以调用FilterChian上doFilter方法, 表示放行过滤器, 接着执行下一个节点
                  如果下一个节点仍然是过滤器, 则接着进行过滤, 执行的过程同上
                  如果没有后续的过滤器, 则执行真正的资源处理这次请求
                  
        --------------------------------------------
        destroy()
            略
        
===================================================          
三.过滤器的应用
    ------------------------------------------------
    3.1.全站乱码解决过滤器
        (1).创建EncodingFilter过滤器类, 实现过滤器接口(Filter)
            详细代码参考: EncodingFilter.java
            
        (2).在web.xml中配置过滤器
            <!-- 配置全站乱码解决过滤器 -->
            <filter>
                <filter-name>EncodingFilter</filter-name>
                <filter-class>cn.tedu.filter.EncodingFilter</filter-class>
            </filter>
            <filter-mapping>
                <filter-name>EncodingFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
        
    ------------------------------------------------
    3.2.自动登陆过滤器实现
        (1).创建AutoLoginFilter过滤器类, 实现过滤器接口(Filter)
            详细代码参考: AutoLoginFilter.java
            
        (2).在web.xml中配置过滤器
            <!-- 配置自动登陆过滤器 -->
            <filter>
                <filter-name>AutoLoginFilter</filter-name>
                <filter-class>cn.tedu.filter.AutoLoginFilter</filter-class>
            </filter>
            <filter-mapping>
                <filter-name>AutoLoginFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>

        (3).在LoginServlet中, 实现30天自动登陆, 将用户名和密码保存进Cookie
            if("true".equals(request.getParameter("autologin"))){
                //实现30天自动登陆
                Cookie cookie = new Cookie("autologin", username+":"+password);
                cookie.setPath(request.getContextPath()+"/");
                cookie.setMaxAge(3600*24*30);
                response.addCookie(cookie);
            }
===================================================
四.//案例--装饰者模式在乱码处理中的应用

public class EncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("全站乱码解决过滤器初始化成功...");
    }
    
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        //1.解决响应正文乱码
        response.setContentType("text/html;charset=utf-8");
        
        //2.解决请求参数乱码 -- (利用装饰设计模式对request对象进行包装)
        HttpServletRequest myReq = new MyHttpServletRequest((HttpServletRequest)request);
    
        //3.放行过滤器
        chain.doFilter(myReq, response);
    }

    public void destroy() {
        
    }
}

class MyHttpServletRequest extends HttpServletRequestWrapper{
    //将request对象保存在类的内部
    private HttpServletRequest request;
    
    //定义flag, 控制getParameterMap()方法中map的遍历次数
    private boolean flag = true;
    
    public MyHttpServletRequest(HttpServletRequest request) {
        super(request);//这行代码千万不要省写!!!
        this.request = request;
    }
    
    public String getParameter(String name) {
        return getParameterValues(name) == null ? null : getParameterValues(name)[0];
    }
    
    public String[] getParameterValues(String name) {
        return (String[]) getParameterMap().get(name);
    }
    
    public Map getParameterMap() {
        try {
            String method = request.getMethod();
            if("POST".equals(method)){//--POST提交
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            }else if("GET".equals(method)){
                //手动编解码解决乱码问题!
                Map<String, String[]> map = request.getParameterMap();
                if(flag){
                    for(Map.Entry<String, String[]> entry : map.entrySet()){
                        String[] vs = entry.getValue();
                        for(int i=0; i<vs.length; i++){
                            vs[i] = new String(vs[i].getBytes("iso8859-1"), "utf-8");
                        }
                    }
                    flag = false;
                }
                return map;
            }else{
                return request.getParameterMap();
            }
            
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

=================================================

五.装饰类详解

1.     request继承结构

ServletRequest(接口)

|-- HttpServletRequest(接口)

|-- 匿名实现类(xxx)  实例: request对象

2.     ServletRequestWrapper装饰类

request对象 --> 被装饰者

ServletRequestWrapper--> 装饰类

 

1.ServletRequestWrapper装饰类 和 被装饰者(request对象)所属的类(xxx)实现了同一个接口(ServletRequest)

 

 

2.提供了构造方法允许将被装饰者传入并保存在了类的内部

 

3.对于不想改造的方法直接调用已有对象上的方法, 对于想要改造的方法直接进行改造(没有对任何方法进行改造), 如:

 

 

3.     HttpServletRequestWrapper装饰类

request对象 --> 被装饰者

HttpServletRequestWrapper -- 装饰类

 

HttpServletRequestWrapper类继承了ServletRequestWrapper装饰类类, 所以HttpServletRequestWrapper也是一个装饰类!!

 

HttpServletRequestWrapper类没有直接去包装request对象, 而是先将当前构造方法中的request对象传给父类(ServletRequestWrapper), 让父类进行包装, 再继承父类中包装后的方法。

而对于自身独有的方法, 自己再进行包装: 通过父类提供的方法(super.getRequest()) 获取 包装后的request对象, 并强制转型为 HttpServletRequest, 并通过提供 _getHttpServletRequest 方法, 方便在当前类的内部使用, 代码如下:

对于HttpServletRequestWrapper类中所有的方法, 直接调 super.getRequest() 对象 -- 即被父类包装后的request对象上的方法

也就是说, 对于HttpServletRequestWrapper装饰类, 是向将自己构造方法中的request对象传给父类(方便父类进行包装), 再通过super.getRequest(); 获取父类中包装的request对象(目的是保证自己和父类包装的是同一个request)

接下来对内部的方法进行包装, 即HttpServletRequestWrapper类中的方法分为两类: 第一类是通过父类继承过来的(父类对于这行方法已经进行包装), 第二类是自己独有的方法, 在自身类的内部进行包装!!





        
        
                

    
    












   

过滤器 & 装饰者模式