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 }