设计模式之代理模式

代理模式有静态代理和动态代理

 

静态代理:提到代理,我们首先想到的就是代理商了,代理商就是替厂家卖产品,这里有三个关键的地方,第一点,卖产品的行为是代理商做的(至少对于消费者来说,消费者接触的就是代理商);第二产品是厂家的(也就是说代理商卖的商品是厂家的,而代理商是没有产品的,从这点来说实质上还是厂家再卖产品)。

回到我们的静态代理上,静态代理有三个角色,委托类(相当于厂家),代理类(相当于代理商),委托类需要实现的接口(产品,只是产品将厂家和代理商联系了起来,同样也是接口将委托类和代理类联系起来了)。了解了这些,静态代理地UML图为:

 

 1.RealSubject是委托类,Proxy是代理类

2.Subject是委托类和代理类实现的接口

3.request()是委托类和代理类的共同方法,相当于卖产品这种行为。

 具体代码如下:

 1 //接口
 2 interface Subject {
 3     void request();
 4 }
 5 
 6 //委托类
 7 class RealSubject implements Subject {
 8     
 9     public void request(){
10         System.out.println("RealSubject");
11     }
12 }
13 
14 //代理类
15 class Proxy implements Subject {
16     private Subject subject;
17    //代理的行为(方法) 
18     public Proxy(Subject subject){
19         this.subject = subject;
20     }
21     public void request(){
22         System.out.println("begin");
23         subject.request();
24         System.out.println("end");
25     }
26 }
27 
28 //测试类
29 public class ProxyTest {
30     public static void main(String args[]) {
31         RealSubject subject = new RealSubject();
32         Proxy p = new Proxy(subject);
33         p.request();
34     }
35 }    

静态代理实现中,代理类在编译期间就已经确定(程序员直接写了出来)。

 

 

动态代理:

代理对象如何产生?

(代理对象的)方法执行过程(如何进行代理的)?进去再出来机制,进去就是利用接口给类传参数来产生需要的代理类,出来就是通过接口,代理类执行自己写的方法。

主要体现在Proxy类中产生代理类的静态方法public static Object getInstance(Class[] interfac,MethodHandler h)

出来时,代理类中,在代理方法中通过MethodHandler变量h ,调用h.invoke(this,Method m);这时方法便执行实现了(自己写的)MethodHandler类的invoke(Object,Method)方法,在MethodHandler类中,我们实现插入的逻辑。

 

动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以代理任何类所实现的接口的任何方法,进行任意的处理。例如:可以代理所有的类而不用像静态代理那样为每个类生成一个代理类,而是动态生成代理类。修改代理的逻辑时(如将时间代理换成日志代理)而不同修改代理类的逻辑,也就是说更加灵活。动态代理更加灵活,代码重用性好,模块化,低内聚。

接下来,用具体类分析:

1.定义委托类实现的接口(接口中的方法也就是要代理的方法)和委托类

1 package dynamic_proxy2;
2 
3 public interface Service {
4     public void add();
5 }
1 package dynamic_proxy2;
2 
3 public class UserServiceImpl implements Service{
4     public void add(){
5         System.out.println("this is an add()");
6     }
7     
8 }

 

2.利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口生成代理

 1 package dynamic_proxy2;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 public class MyInvocationHandler implements InvocationHandler{
 8     private Object target;//委托对象
 9     
10     public MyInvocationHandler(Object target){
11         this.target = target;
12     }
13     
14     /**
15      * 此方法由生成的代理类调用,并生成调用方法的方法对象,并传入到此方法,此后在此方法中由原生类进行反射调用
16      */
17     @Override
18     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
19         System.out.println("add() begin");
20         Object result = method.invoke(target,args);//反射
21         System.out.println("add() end");
22         return result;
23     }
24     
25     public Object getProxy(){
26         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
27         Class<?>[] interfaces = target.getClass().getInterfaces();
28         
29         /**
30          * classLoader:用ContextClassLoader进行对代理类进行加载
31          * interfaces:原生类实现的接口
32          * this:实现了InvocationHandler接口的MyInvocationHandler(自己写的),
33          * 这个对象对传入到代理类的InvocationHandler变量中
34          */
35         return Proxy.newProxyInstance(classLoader, interfaces, this);
36     }
37 
38 }

 

3.使用动态代理:

 1 package dynamic_proxy2;
 2 
 3 public class ProxyTest {
 4 
 5     public static void main(String[] args) {
 6         Service service = new UserServiceImpl();//原生类
 7         
 8         MyInvocationHandler handler = new MyInvocationHandler(service);
 9         Service serviceproxy = (Service) handler.getProxy();//获得代理对象
10         
11         /**
12          * 生成add()对象 m,并调用InvocationHandler变量的invoke(this,m)
13          */
14         serviceproxy.add();
15     }
16 
17 }

  以上的这些也就是程序员需要编写的,动态代理的核心Proxy类和InvocationHandler接口已经由JDK实现了。

接下来看看运行结果

1 add() begin
2 this is an add()
3 add() end

然后我们借用这个例子再具体说说,动态代理的特点与好处:

1.若我们想对任何的类进行代理,我们只要创建这个类即可,然后生成该类的对象作为参数传给实现了InvocationHandler接口的MyInvocationHandler类的构造器方法。

2.若我们相对委托类进行任何的处理只需在MyInvocationHandler类中的invoke方法(此方法即是实现的InvocationHandler的方法)中写入处理代码。

了解了这些后,我们就该谈谈代理类是如何产生的?以及代理类如何进行代理的?

首先,我们来谈谈代理类的产生,代理类(对象)的生成过程是由Proxy类的newProxyInstance方法实现,分为三个步骤:

1.ProxyGenerator.generateProxyClass方法负责生成代理类的字节码,在我的模拟JDK动态代理实现中,是生成代理类源码,这个代理类源码由委托类实现的接口,类加载器组装而成。

2.native方法Proxy.defineClass0负责字节码的加载,并返回对应的Class对象。在模拟实验中,是利用Java提供的编译器,加载器,对代理类进行编译和加载。

3.利用反射机制生成代理类的对象。在模拟实验中,通过反射机制,得到带有委托类实现接口参数的构造器对象,并利用这个对象生成代理类的对象。代码如:

1 //ul为类加载器,方法里的参数为生成的代理类的源码
2 Class c = ul.loadClass("com.bjsxt.proxy.TankTimeProxy");
3 //Moveable为委托类,代理类实现的接口        
4 Constructor ctr = c.getConstructor(Moveable.class);
5 //得到代理类的对象
6 Moveable m = (Moveable)ctr.newInstance(new Tank());

通过反编译得到代理类的源码:

 1 public final class $proxy1 extends Proxy implements Service {
 2 
 3     public $proxy1(InvocationHandler invocationhandler) {
 4         super(invocationhandler);
 5     }
 6 
 7     public final boolean equals(Object obj) {
 8         try {
 9             return ((Boolean)super.h.invoke(this, m1, new Object[] {
10                 obj
11             })).booleanValue();
12         }
13         catch(Error _ex) { }
14         catch(Throwable throwable) {
15             throw new UndeclaredThrowableException(throwable);
16         }
17     }
18 
19     public final String toString() {
20         try {
21             return (String)super.h.invoke(this, m2, null);
22         }
23         catch(Error _ex) { }
24         catch(Throwable throwable) {
25             throw new UndeclaredThrowableException(throwable);
26         }
27     }
28 
29     public final void add() {
30         try {
31             super.h.invoke(this, m3, null);
32             return;
33         }
34         catch(Error _ex) { }
35         catch(Throwable throwable) {
36             throw new UndeclaredThrowableException(throwable);
37         }
38     }
39 
40     public final int hashCode() {
41         try {
42             return ((Integer)super.h.invoke(this, m0, null)).intValue();
43         }
44         catch(Error _ex) { }
45         catch(Throwable throwable) {
46             throw new UndeclaredThrowableException(throwable);
47         }
48     }
49 
50     private static Method m1;
51     private static Method m2;
52     private static Method m3;
53     private static Method m0;
54 
55     static {
56         try {
57             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
58                 Class.forName("java.lang.Object")
59             });
60             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
61             m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
62             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
63         }
64         catch(NoSuchMethodException nosuchmethodexception) {
65             throw new NoSuchMethodError(nosuchmethodexception.getMessage());
66         }
67         catch(ClassNotFoundException classnotfoundexception) {
68             throw new NoClassDefFoundError(classnotfoundexception.getMessage());
69         }
70     }
71 }
72 

根据我们前面的分析,生成的代理类中应该有InvocationHandler变量,实现了代理方法会调用子类(也就是我们实现InvocationHandler接口的类)的invoke方法。

而从上面的代码中并没有发现有InvocationHandler变量,但是它(生成的代理类)继承自Proxy类,接下来看一下Proxy类

 1 public class Proxy implements java.io.Serializable {
 2 
 3     private static final long serialVersionUID = -2222568056686623797L;
 4 
 5     /** parameter types of a proxy class constructor */
 6     private static final Class<?>[] constructorParams =
 7         { InvocationHandler.class };
 8 
 9     /**
10      * a cache of proxy classes
11      */
12     private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
13         proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
14 
15     /**
16      * the invocation handler for this proxy instance.
17      * @serial
18      */
19     protected InvocationHandler h;

在代码的最后,我们发现了InvocationHandler变量h。由此代理方法的整个调用过程就是,

1,代理类调用代理方法,在代理方法内,得到该方法对象,

2,调用InvocationHandler变量h的invoke方法,传入代理类,代理方法对象,代理方法参数,

 此时h引用的是我们实现InvocationHandler接口的子类,这步操作是在zileidegetProxy()方法里

 1 public Object getProxy(){
 2         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 3         Class<?>[] interfaces = target.getClass().getInterfaces();
 4         
 5         /**
 6          * classLoader:用ContextClassLoader进行对代理类进行加载
 7          * interfaces:原生类实现的接口
 8          * this:实现了InvocationHandler接口的MyInvocationHandler(自己写的),
 9          * 这个对象对传入到代理类的InvocationHandler变量中
10          */
11         return Proxy.newProxyInstance(classLoader, interfaces, this);
12     }

 

super.h.invoke(this, m3, null);

JDK动态代理的局限性
通过反射类Proxy和InvocationHandler接口实现的动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类

 便无法使用这种方式实现动态代理。而且就算实现了接口也只能代理实现了接口的方法,委托类原有的类仍然不能被代理。

 

 

 

参考:https://www.jianshu.com/p/a1d094fc6c00

posted @ 2017-12-29 21:35  流沙若水  阅读(212)  评论(0编辑  收藏  举报