1.什么是代理?
大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。

2.什么情况下使用代理?
(1)设计模式中有一个设计原则是,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
(2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是的一种表现。
(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程

3.静态代理和动态代理
我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

4.静态代理
我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。

public interface Person {
	public void sayHello();
	public int getAge();
}
public class Tom implements Person {

	public void sayHello() {
		// TODO Auto-generated method stub
		System.out.println("Tom say Hello!!!");
	}

	public int getAge() {
		// TODO Auto-generated method stub
		System.out.println("tom age 20");
		return 20;
	}
}
public class StaticProxyDemo implements Person{
	private Tom tom;

	public StaticProxyDemo(Tom tom){
		this.tom=tom;
	}

	public void sayHello() {
		// TODO Auto-generated method stub
		System.out.println("tom sayHello begin!");
		tom.sayHello();
		System.out.println("tom sayHello end!");
	}

	public int getAge() {
		// TODO Auto-generated method stub
		System.out.println("tom getAge begin!");
		int age = tom.getAge();
		System.out.println("tom getAge begin!");
		return age;
	}
	public static void main(String[] args) {
		Tom tom = new Tom();
		StaticProxyDemo staticProxyDemo = new StaticProxyDemo(tom);
		staticProxyDemo.sayHello();
		staticProxyDemo.getAge();
	}
}

控制台输出:

tom sayHello begin!
Tom say Hello!!!
tom sayHello end!
tom getAge begin!
tom age 20
tom getAge begin!

静态代理看起来是比较简单的,没有什么问题只不过是在代理类中引入了被代理类的对象而已。
那么接下来我们看看动态代理。

5.动态代理
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:
InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance,
 the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

Proxy类:
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

我们看这个方法的三个参数:
ClassLoader loader:指定一个动态加载代理类的类加载器
Class<?>[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法。
InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。
(2)实例:
上面的Person接口和Student被代理类保持不变。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy implements InvocationHandler {
	private Object person;
	public DynamicProxy(Object person){
		this.person = person;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		 //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("调用目标对象前");
        System.out.println("Method:" + method);

        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object invoke = method.invoke(person, args);

        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("调用目标对象后");
		return invoke;
	}
	public static void main(String[] args) {
		Tom tom = new Tom();
		DynamicProxy dynamicProxy = new DynamicProxy(tom);
		Person tomProxy =(Person) Proxy.newProxyInstance(
				DynamicProxy.class.getClassLoader(),
				tom.getClass().getInterfaces(),
				dynamicProxy);
		tomProxy.sayHello();
		tomProxy.getAge();
	}
}

控制台输出:

调用目标对象前
Method:public abstract void cn.ithiema.dynamicproxy.Person.sayHello()
Tom say Hello!!!
调用目标对象后
调用目标对象前
Method:public abstract int cn.ithiema.dynamicproxy.Person.getAge()
tom age 20
调用目标对象后

6.动态代理的应用
(1)在字符过滤器中使用动态代理解决中文乱码

  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 /**
18 * @ClassName: CharacterEncodingFilter
19 * @Description: 解决中文乱码的字符过滤器
20 * @author:
21 * @date:
22 *
23 */
24 public class CharacterEncodingFilter implements Filter {
25
26     @Override
27     public void init(FilterConfig filterConfig) throws ServletException {
28
29     }
30
31     @Override
32     public void doFilter(ServletRequest req, ServletResponse resp,
33             FilterChain chain) throws IOException, ServletException {
34
35         final HttpServletRequest request = (HttpServletRequest) req;
36         HttpServletResponse response = (HttpServletResponse) resp;
37         //解决以Post方式提交的中文乱码问题
38         request.setCharacterEncoding("UTF-8");
39         response.setCharacterEncoding("UTF-8");
40         response.setContentType("text/html;charset=UTF-8");
41         //获取获取HttpServletRequest对象的代理对象
42         ServletRequest requestProxy = getHttpServletRequestProxy(request);
43         /**
44          * 传入代理对象requestProxy给doFilter方法,
45          * 这样用户在使用request对象时实际上使用的是HttpServletRequest对象的代理对象requestProxy
46          */
47         chain.doFilter(requestProxy, response);
48     }
49
50
51     /**
52     * @Method: getHttpServletRequestProxy
53     * @Description: 获取HttpServletRequest对象的代理对象
56     * @param request
57     * @return HttpServletRequest对象的代理对象
58     */
59     private ServletRequest getHttpServletRequestProxy(final HttpServletRequest request){
60         ServletRequest proxy  = (ServletRequest) Proxy.newProxyInstance(
61                 CharacterEncodingFilter.class.getClassLoader(),
62                 request.getClass().getInterfaces(),
63                 new InvocationHandler(){
64                     @Override
65                     public Object invoke(Object proxy, Method method, Object[] args)
66                             throws Throwable {
67                         //如果请求方式是get并且调用的是getParameter方法
68                         if (request.getMethod().equalsIgnoreCase("get") && method.getName().equals("getParameter")) {
69                             //调用getParameter方法获取参数的值
70                             String value = (String) method.invoke(request, args);
71                             if(value==null){
72                                 return null;
73                             }
74                             //解决以get方式提交的中文乱码问题
75                             return new String(value.getBytes("iso8859-1"),"UTF-8");
76                         }else {
77                             //直接调用相应的方法进行处理
78                             return method.invoke(request, args);
79                         }
80                     }
81                 });
82         //返回HttpServletRequest对象的代理对象
83         return proxy;
84     }
85
86     @Override
87     public void destroy() {
88
89     }
90 }
10-06 13:40