Java静态代理和动态代理

一、代理

  概述:代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。

  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

二、静态代理

  静态代理的实现比较简单,代理类通过实现与目标对象相同的接口,并在类中维护一个代理对象。通过构造器塞入目标对象,赋值给代理对象,进而执行代理对象实现的接口方法,并实现前拦截,后拦截等所需的业务功能。

演示代码:

/**
 * 目标对象实现的接口
 * @author BalmyLee
 */
public interface BussinessInterface {
    void execute();
}
/**
 * 目标对象实现类
 * @author BalmyLee
 */
public class Bussiness implements BussinessInterface{
 
    @Override
    public void execute() {
        System.out.println("执行业务逻辑...");
    }
}
/**
 * 代理类,通过实现与目标对象相同的接口
 * 并维护一个代理对象,通过构造器传入实际目标对象并赋值
 * 执行代理对象实现的接口方法,实现对目标对象实现的干预
 * @author BalmyLee
 */
public class BussinessProxy implements BussinessInterface{
     
    private BussinessInterface bussinessImpl;
     
    public BussinessProxy(BussinessInterface bussinessImpl) {
        this.bussinessImpl = bussinessImpl;
    }
     
    @Override
    public void execute() {
        System.out.println("前拦截...");
        bussinessImpl.execute();
        System.out.println("后拦截...");
    }
}

总结:

  优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
  缺点:因为代理对象,需要实现与目标对象一样的接口,会导致代理类十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。

三、动态代理

  动态代理是指动态的在内存中构建代理对象(需要我们制定要代理的目标对象实现的接口类型),即利用JDK的API生成指定接口的对象,也称之为JDK代理或者接口代理。

  • 演示代码

1、定义对象行为接口

/**
 * Project : balmy-admin
 * Package:com.balmy.proxy.bean
 * Title:Person.java
 * <p> Description: </p>
 * @author BalmyLee
 * @version 1.0
 * @data 2019年5月25日
 */
public interface Person {
    
    /**
     * <p>Description: 唱歌</p>
     * @author BalmyLee
     * @date 2019年5月25日
     * @param name 歌曲名称
     * @return
     */
    String sing(String name);
    
    /**
     * <p>Description: 跳舞</p>
     * @author BalmyLee
     * @date 2019年5月25日
     * @param name 舞蹈名称
     * @return
     */
    String dance(String name);
}

2、定义目标业务实现类

/**
 * Project : balmy-admin
 * Package:com.balmy.proxy.service.impl
 * Title:LayZhang.java
 * <p> Description: </p>
 * @author BalmyLee
 * @version 1.0
 * @data 2019年5月25日
 */
public class LayZhang implements Person{

    @Override
    public String sing(String name) {
        System.out.println("singing "+ name+ " !!!");
        return name+" singing has ended, thank you!";
    }

    @Override
    public String dance(String name) {
        System.out.println(" dncing "+ name+ " !!!");
        return name+" dance has ended, thank you!";
    }

}

3、创建生成代理对象的代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/**
 * Project : balmy-admin
 * Package:com.balmy.proxy
 * Title:LayZhangProxy.java
 * <p> Description:  这个代理类负责生成刘德华的代理人</p>
 * @author BalmyLee
 * @version 1.0
 * @data 2019年5月25日
 */
public class LayZhangProxy {
    
    //设计一个类变量记住代理类要代理的目标对象
    private Person person = new LayZhang();
    
    
    /**
     *  设计一个方法生成代理对象
     * <p>Description: 这个方法返回刘德华的代理对象:Person person = BalmyLeeProxy.getProxy();//得到一个代理对象 </p>
     * @author BalmyLee
     * @date 2019年5月25日
     * @return 某个对象的代理对象
     */
     public Person getProxy() {
         
        // 使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某个对象的代理对象
        return (Person) Proxy.newProxyInstance(LayZhangProxy.class.getClassLoader(), person.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * <p>
                     * Description:
                     * InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,我们不用单独去定义一个类来实现该接口,而是直接使用一个匿名内部类来实现该接口,
                     * new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类 
                      *  在invoke方法编码指定返回的代理对象干的工作 , 当调用代理对象的person.sing("冰雨");或者 person.dance("江南style");方法时, 
                      *  实际上执行的都是invoke方法里面的代码,因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
                     * </p>
                     * 
                     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
                     *      java.lang.reflect.Method, java.lang.Object[])
                     * @author BalmyLee
                     * @date 2019年5月25日
                     * @param proxy  把代理对象自己传递进来
                     * @param method 把代理对象当前调用的方法传递进来
                     * @param args   把方法参数传递进来
                     * @return
                     * @throws Throwable
                     *
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("sing")) {
                            System.out.println("I am his agent, asking him to sing first to give 100,000 yuan! !");
                            // 已经给钱了,经纪人自己不会唱歌,就只能找张艺兴唱歌
                            return method.invoke(person, args); // 代理对象调用真实目标对象的sing方法去处理用户请求
                        }
                        // 如果调用的是代理对象的dance方法
                        if (method.getName().equals("dance")) {
                            System.out.println("I am his agent, I want him to dance first to give 200,000 yuan! !");
                            // 已经给钱了,经纪人自己不会跳舞,就只能找张艺兴去跳舞!
                            return method.invoke(person, args);// 代理对象调用真实目标对象的dance方法去处理用户请求
                        }
                        return null;
                    }
                });
     }

}

4.、测试结果

import org.junit.Test;public class ProxyTest {
    /**
     * <p>Description: 测试</p>
     * @author BalmyLee
     * @date 2019年5月26日
     */
    @Test
    public void test() {
        LayZhangProxy proxy = new LayZhangProxy();
        //获得代理对象
        Person p = proxy.getProxy();
        //调用代理对象的sing方法
        String retValue = p.sing("《一个人》");
        System.out.println(retValue);
        //调用代理对象的dance方法
        String value = p.dance("《梦不落雨林》");
        System.out.println(value);
    }

}

5、查看结果

I am his agent, asking him to sing first to give 100,000 yuan! !
singing 《一个人》 !!!
《一个人》 singing has ended, thank you!
I am his agent, I want him to dance first to give 200,000 yuan! !
 dncing 《梦不落雨林》 !!!
《梦不落雨林》 dance has ended, thank you!

  Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
  由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

  •  应用实例

  在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。

  1、在字符过滤器中使用动态代理解决中文乱码

 1 package com.balmy.proxy.filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 /**
12  * Project : HelloServlet
13  * Package:com.balmy.proxy.filter
14  * Title:CharacterEncodingFilter.java
15  * <p> Description:  解决中文乱码的字符过滤器</p>
16  * @author BalmyLee
17  * @version 1.0
18  * @data 2019年5月27日
19  */
20 public class CharacterEncodingFilter implements Filter{
21 
22     /**
23      * <p>Description: 在销毁Filter时自动调用。</p>
24      * @see javax.servlet.Filter#destroy()
25      * @author BalmyLee
26      * @date 2019年5月27日
27      */
28     @Override
29     public void destroy() {
30         // TODO Auto-generated method stub
31         
32     }
33 
34     /**
35      * <p>Description: 拦截到要执行的请求时,doFilter就会执行。这里面写我们对请求和响应的预处理</p>
36      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
37      * @author BalmyLee
38      * @date 2019年5月27日
39      * @param request
40      * @param response
41      * @param chain
42      * @throws IOException
43      * @throws ServletException
44      *
45      */
46     @Override
47     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
48             throws IOException, ServletException {
49         // 解决以Post方式提交的中文乱码问题
50         request.setCharacterEncoding("UTF-8");
51         response.setCharacterEncoding("UTF-8");
52         response.setContentType("text/html;charset=UTF-8");
53         chain.doFilter(request, response);
54 
55     }
56 
57     /**
58      * <p>Description: init()方法:初始化参数,在创建Filter时自动调用。当我们需要设置初始化参数的时候,可以写到该方法中。</p>
59      * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
60      * @author BalmyLee
61      * @date 2019年5月27日
62      * @param filterConfig
63      * @throws ServletException
64      */
65     @Override
66     public void init(FilterConfig filterConfig) throws ServletException {
67         // TODO Auto-generated method stub
68         
69     }
70 
71 }

  但是这种写法是没有办法解决以get方式提交中文参数时的乱码问题的,我们可以用如下的代码来证明上述的解决中文乱码过滤器只对以post方式提交中文参数时有效,而对于以get方式提交中文参数时无效

  jsp测试页面如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--引入jstl标签库 --%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<title>使用字符过滤器解决解决get、post请求方式下的中文乱码问题</title>
</head>
<body>
    <%--使用c:url标签构建url,构建好的url存储在servletDemo1变量中--%>
    <c:url value="/user/UserInfo" scope="page" var="UserInfo">
        <%--构建的url的附带的中文参数 ,参数名是:username,值是:木子婉--%>
        <c:param name="username" value="木子婉"></c:param>
    </c:url>
    <%--使用get的方式访问 --%>
    <a href="${UserInfo}">超链接(get方式请求)</a>
    <hr />
    <%--使用post方式提交表单 --%>
    <form action="${pageContext.request.contextPath}/user/UserInfo"    method="post">
        <label>用户名:</label>
        <input type="text" name="username" value="木子婉" /> 
        <input type="submit" value="post方式提交">
    </form>

</body>
</html>

  处理请求的UserServlet代码如下:

 1 package com.balmy.proxy.servlet;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.annotation.WebServlet;
 8 import javax.servlet.http.HttpServlet;
 9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 
12 /**
13  * Project : ProxyDemo
14  * Package:com.balmy.proxy.servlet
15  * Title:UserServlet.java
16  * <p> Description: </p>
17  * @author BalmyLee
18  * @version 1.0
19  * @data 2019年5月27日
20  */
21 22 public class UserServlet extends HttpServlet {
23     private static final long serialVersionUID = 1L;
24        
25     /**
26      * @see HttpServlet#HttpServlet()
27      */
28     public UserServlet() {
29         super();
30         // TODO Auto-generated constructor stub
31     }
32 
33     
34     /**
35      * <p>Description: doGet</p>
36      * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
37      * @author BalmyLee
38      * @date 2019年5月27日
39      * @param request
40      * @param response
41      * @throws ServletException
42      * @throws IOException
43      */
44     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
45          // 接收参数
46         String username = request.getParameter("username");
47         // 获取请求方式
48         String method = request.getMethod();
49         // 获取输出流
50         PrintWriter out = response.getWriter();
51         out.write("请求的方式:" + method);
52         out.write("<br/>");
53         out.write("接收到的参数:" + username);
54     }
55 
56     /**
57      * <p>Description: doPost </p>
58      * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
59      * @author BalmyLee
60      * @date 2019年5月27日
61      * @param request
62      * @param response
63      * @throws ServletException
64      * @throws IOException
65      */
66     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
67         doGet(request, response);
68     }
69 
70 }

   在web.xml中注册上述的CharacterEncodingFilter和UserServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>ProxyDemo</display-name>
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.balmy.proxy.filter.CharacterEncodingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.balmy.proxy.servlet.UserServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/user/UserInfo</url-pattern>
    </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

  测试结果如下所示:

  从运行结果可以看出,上述的过滤器的确是不能解决以get方式提交中文参数的乱码问题,下面使用动态代理技术改造上述的过滤器,使之能够解决以get方式提交中文参数的乱码问题,改造后的过滤器代码如下:

  1 package com.balmy.proxy.filter;
  2 
  3 import java.io.IOException;
  4 import java.lang.reflect.InvocationHandler;
  5 import java.lang.reflect.Method;
  6 import java.lang.reflect.Proxy;
  7 
  8 import javax.servlet.Filter;
  9 import javax.servlet.FilterChain;
 10 import javax.servlet.FilterConfig;
 11 import javax.servlet.ServletException;
 12 import javax.servlet.ServletRequest;
 13 import javax.servlet.ServletResponse;
 14 import javax.servlet.http.HttpServletRequest;
 15 import javax.servlet.http.HttpServletResponse;
 16 /**
 17  * Project : HelloServlet
 18  * Package:com.balmy.proxy.filter
 19  * Title:CharacterEncodingFilter.java
 20  * <p> Description:  解决中文乱码的字符过滤器</p>
 21  * @author BalmyLee
 22  * @version 1.0
 23  * @data 2019年5月27日
 24  */
 25 public class CharacterEncodingFilter implements Filter{
 26 
 27     /**
 28      * <p>Description: 在销毁Filter时自动调用。</p>
 29      * @see javax.servlet.Filter#destroy()
 30      * @author BalmyLee
 31      * @date 2019年5月27日
 32      */
 33     @Override
 34     public void destroy() {
 35         // TODO Auto-generated method stub
 36         
 37     }
 38 
 39     /**
 40      * <p>Description: 拦截到要执行的请求时,doFilter就会执行。这里面写我们对请求和响应的预处理</p>
 41      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
 42      * @author BalmyLee
 43      * @date 2019年5月27日
 44      * @param request
 45      * @param response
 46      * @param chain
 47      * @throws IOException
 48      * @throws ServletException
 49      *
 50      */
 51     @Override
 52     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
 53             throws IOException, ServletException {
 54         final HttpServletRequest request = (HttpServletRequest) req;
 55         HttpServletResponse response = (HttpServletResponse) resp;
 56         // 解决以Post方式提交的中文乱码问题
 57         request.setCharacterEncoding("UTF-8");
 58         response.setCharacterEncoding("UTF-8");
 59         response.setContentType("text/html;charset=UTF-8");
 60         // 获取获取HttpServletRequest对象的代理对象
 61         ServletRequest requestProxy = getHttpServletRequestProxy(request);
 62         /**
 63          * 传入代理对象requestProxy给doFilter方法,
 64          * 这样用户在使用request对象时实际上使用的是HttpServletRequest对象的代理对象requestProxy
 65          */
 66         chain.doFilter(requestProxy, response);
 67 
 68     }
 69 
 70     /**
 71      * <p>Description: 获取HttpServletRequest对象的代理对象</p>
 72      * @author BalmyLee
 73      * @date 2019年5月27日
 74      * @param request
 75      * @return HttpServletRequest对象的代理对象
 76      */
 77     private ServletRequest getHttpServletRequestProxy(HttpServletRequest request) {
 78         ServletRequest proxy = (ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(),
 79                 request.getClass().getInterfaces(), new InvocationHandler() {
 80                     @Override
 81                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 82                         // 如果请求方式是get并且调用的是getParameter方法
 83                         if (request.getMethod().equalsIgnoreCase("get") && method.getName().equals("getParameter")) {
 84                             // 调用getParameter方法获取参数的值
 85                             String value = (String) method.invoke(request, args);
 86                             if (value == null) {
 87                                 return null;
 88                             }
 89                             // 解决以get方式提交的中文乱码问题
 90                             return new String(value.getBytes("iso8859-1"), "UTF-8");
 91                         } else {
 92                             // 直接调用相应的方法进行处理
 93                             return method.invoke(request, args);
 94                         }
 95                     }
 96                 });
 97         // 返回HttpServletRequest对象的代理对象
 98         return proxy;
 99     }
100 
101     /**
102      * <p>Description: init()方法:初始化参数,在创建Filter时自动调用。当我们需要设置初始化参数的时候,可以写到该方法中。</p>
103      * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
104      * @author BalmyLee
105      * @date 2019年5月27日
106      * @param filterConfig
107      * @throws ServletException
108      */
109     @Override
110     public void init(FilterConfig filterConfig) throws ServletException {
111         // TODO Auto-generated method stub
112         
113     }
114 
115 }

  我们在过滤器中使用动态代理技术生成一个HttpServletRequest对象的代理对象requestProxy,然后把代理对象requestProxy进行chain.doFilter(requestProxy, response)传递给用户使用,这样用户实际上使用的就是HttpServletRequest对象的代理对象requestProxy。然而这一过程对于用户来说是透明的,用户是不知道自己使用的HttpServletRequest对象是一个代理对象requestProxy,由于代理对象requestProxy和目标对象HttpServletRequest具有相同的方法,当用户调用getParameter方法接收中文参数时,实际上调用的就是代理对象requestProxy的invoke方法,因此我们就可以在invoke方法中就判断当前的请求方式以及用户正在调用的方法,如果判断当前的请求方式是get方式并且用户正在调用的是getParameter方法,那么我们就可以手动处理get方式提交中文参数的中文乱码问题了。
  测试结果如下所示:

  2、在字符过滤器中使用动态代理压缩服务器响应的内容后再输出到客户端

  压缩过滤器的代码如下:

  1 package com.balmy.proxy.filter;
  2 
  3 import java.io.ByteArrayOutputStream;
  4 import java.io.IOException;
  5 import java.io.OutputStreamWriter;
  6 import java.io.PrintWriter;
  7 import java.lang.reflect.InvocationHandler;
  8 import java.lang.reflect.Method;
  9 import java.lang.reflect.Proxy;
 10 import java.util.zip.GZIPOutputStream;
 11 
 12 import javax.servlet.Filter;
 13 import javax.servlet.FilterChain;
 14 import javax.servlet.FilterConfig;
 15 import javax.servlet.ServletException;
 16 import javax.servlet.ServletOutputStream;
 17 import javax.servlet.ServletRequest;
 18 import javax.servlet.ServletResponse;
 19 import javax.servlet.WriteListener;
 20 import javax.servlet.http.HttpServletRequest;
 21 import javax.servlet.http.HttpServletResponse;
 22 
 23 /**
 24  * Project : ProxyDemo
 25  * Package:com.balmy.proxy.filter
 26  * Title:GzipFilter.java
 27  * <p> Description: 压缩过滤器,将web应用中的文本都经过压缩后再输出到浏览器</p>
 28  * @author BalmyLee
 29  * @version 1.0
 30  * @data 2019年5月28日
 31  */
 32 public class GzipFilter implements Filter {
 33 
 34     @Override
 35     public void destroy() {
 36         // TODO Auto-generated method stub
 37         
 38     }
 39 
 40     @Override
 41     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
 42             throws IOException, ServletException {
 43         final HttpServletRequest request = (HttpServletRequest) req;
 44         final HttpServletResponse response = (HttpServletResponse) resp;
 45         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
 46         final PrintWriter pw = new PrintWriter(new OutputStreamWriter(bout, "UTF-8"));
 47 
 48         chain.doFilter(request, getHttpServletResponseProxy(response, bout, pw));
 49         pw.close();
 50         // 拿到目标资源的输出
 51         byte result[] = bout.toByteArray();
 52         System.out.println("原始大小:" + result.length);
 53 
 54         ByteArrayOutputStream bout2 = new ByteArrayOutputStream();
 55         GZIPOutputStream gout = new GZIPOutputStream(bout2);
 56         gout.write(result);
 57         gout.close();
 58 
 59         // 拿到目标资源输出的压缩数据
 60         byte gzip[] = bout2.toByteArray();
 61         System.out.println("压缩大小:" + gzip.length);
 62 
 63         response.setHeader("content-encoding", "gzip");
 64         response.setContentLength(gzip.length);
 65         response.getOutputStream().write(gzip);
 66 
 67     }
 68 
 69     /**
 70      * <p>Description: 获取HttpServletResponse对象的代理对象</p>
 71      * @author BalmyLee
 72      * @date 2019年5月28日
 73      * @param response
 74      * @param bout
 75      * @param pw
 76      * @return HttpServletResponse对象的代理对象
 77      */
 78     private ServletResponse getHttpServletResponseProxy(HttpServletResponse response, ByteArrayOutputStream bout,
 79             PrintWriter pw) {
 80         return (ServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(),
 81                 response.getClass().getInterfaces(), new InvocationHandler() {
 82                     @Override
 83                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 84                         if (method.getName().equals("getWriter")) {
 85                             return pw;
 86                         } else if (method.getName().equals("getOutputStream")) {
 87                             return new MyServletOutputStream(bout);
 88                         } else {
 89                             return method.invoke(response, args);
 90                         }
 91                     }
 92                 });
 93     }
 94 
 95     @Override
 96     public void init(FilterConfig filterConfig) throws ServletException {
 97         // TODO Auto-generated method stub
 98 
 99     }
100 
101 }
102 
103 class MyServletOutputStream extends ServletOutputStream{
104     
105     private ByteArrayOutputStream  bout = null;
106     public MyServletOutputStream(ByteArrayOutputStream  bout){
107         this.bout = bout;
108     }
109     @Override
110     public void write(int b) throws IOException {
111         bout.write(b);
112     }
113     
114     @Override
115     public boolean isReady() {
116         // TODO Auto-generated method stub
117         return false;
118     }
119     
120     @Override
121     public void setWriteListener(WriteListener arg0) {
122         // TODO Auto-generated method stub
123         
124     }
125     
126 }

  在web.xml中注册上述的GzipFilter:

  <filter>
        <description>配置压缩过滤器</description>
        <filter-name>GzipFilter</filter-name>
        <filter-class>me.gacl.web.filter.GzipFilter</filter-class>
    </filter>

    <!--jsp文件的输出的内容都经过压缩过滤器压缩后才输出 -->
    <filter-mapping>
        <filter-name>GzipFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <!-- 配置过滤器的拦截方式 -->
        <!-- 对于在Servlet中通过 request.getRequestDispatcher("jsp页面路径").forward(request, 
            response) 方式访问的Jsp页面的要进行拦截 -->
        <dispatcher>FORWARD</dispatcher>
        <!--对于直接以URL方式访问的jsp页面进行拦截,过滤器的拦截方式默认就是REQUEST -->
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    <!--js文件的输出的内容都经过压缩过滤器压缩后才输出 -->
    <filter-mapping>
        <filter-name>GzipFilter</filter-name>
        <url-pattern>*.js</url-pattern>
    </filter-mapping>
    <!--css文件的输出的内容都经过压缩过滤器压缩后才输出 -->
    <filter-mapping>
        <filter-name>GzipFilter</filter-name>
        <url-pattern>*.css</url-pattern>
    </filter-mapping>
    <!--html文件的输出的内容都经过压缩过滤器压缩后才输出 -->
    <filter-mapping>
        <filter-name>GzipFilter</filter-name>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>

 

总结:

  优点:代理对象无需实现接口,免去了编写很多代理类的烦恼,同时接口增加方法也无需再维护目标对象和代理对象,只需在事件处理器中添加对方法的判断即可。
  缺点:代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK动态代理。

 

posted @ 2019-05-26 02:14  BalmyLee  阅读(262)  评论(0)    收藏  举报