代理模式及JDK动态代理(InvocationHandler)的简单实现与分析

原文地址:https://blog.csdn.net/hello2mao/article/details/52346205

在慕课网上学习了讲解代理模式的一个课程--《模式的秘密--代理模式》,感叹于David老师屌炸天的PPT,同时,老师一步一步模仿JDK源码去写code,教我们去简单实现JDK中的动态代理,讲的清晰透彻。在此我做下笔记,课程原地址:http://www.imooc.com/learn/214

一、概述

代理模式定义:为其他对象提供一种代理,以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或增加额外的服务。

常见的代理模式:

(1)远程代理:给一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。

(2)虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。e.g. 刷网页,先显示预先准备好的背景图,等真正的图片加载完成后再显示。

(3)保护代理:控制用户的访问权限

(4)智能引用代理:提供对目标对象额外的服务

代理的实现方式:静态代理、动态代理


二、静态代理

代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类。类图如下:


举例如下:

[java]  view plain  copy
  1. package com.test.proxytest;  
  2.   
  3. //抽象对象角色  
  4. interface IMacBook {  
  5.     void buyIt();  
  6. }  
  7. //目标对象角色  
  8. class HangKangMacBook implements IMacBook {  
  9.     @Override  
  10.     public void buyIt() {  
  11.         System.out.println("This computer is from HangKang!");  
  12.     }  
  13. }  
  14. //代理对象角色  
  15. class ProxyMacBook implements IMacBook {  
  16.     @Override  
  17.     public void buyIt() {  
  18.         HangKangMacBook mac = new HangKangMacBook();  
  19.         mac.buyIt();  
  20.     }  
  21. }  
  22. //客户端  
  23. public class Main {  
  24.     public static void main(String[] args) {  
  25.         IMacBook macBook = new ProxyMacBook();  
  26.         macBook.buyIt();  
  27.     }  
  28. }  

三、JDK动态代理

Java动态代理位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler

该接口中仅定义了一个方法:public Object invoke(Object obj, Method method, Object[] args),在使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy

该类即为动态代理类,static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h),返回代理类的一个实例,返回后的代理类可以当作被代理类使用。


JDK动态代理的一般实现步骤如下:

(1)创建一个实现InvocationHandler接口的类,它必须实现invoke方法

(2)创建被代理的类以及接口

(3)调用Proxy的静态方法newProxyInstance,创建一个代理类

(4)通过代理调用方法

举例如下:

[java]  view plain  copy
  1. public interface Moveable {  
  2.     void move();  
  3. }  
[java]  view plain  copy
  1. public class Car implements Moveable {  
  2.   
  3.     @Override  
  4.     public void move() {  
  5.         //实现开车  
  6.         try {  
  7.             Thread.sleep(new Random().nextInt(1000));  
  8.             System.out.println("汽车行驶中....");  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.     }  
  13.   
  14. }  
[java]  view plain  copy
  1. public class TimeHandler implements InvocationHandler {  
  2.   
  3.     public TimeHandler(Object target) {  
  4.         super();  
  5.         this.target = target;  
  6.     }  
  7.   
  8.     private Object target;  
  9.       
  10.     /* 
  11.      * 参数: 
  12.      * proxy  被代理对象 
  13.      * method  被代理对象的方法 
  14.      * args 方法的参数 
  15.      *  
  16.      * 返回值: 
  17.      * Object  方法的返回值 
  18.      * */  
  19.     @Override  
  20.     public Object invoke(Object proxy, Method method, Object[] args)  
  21.             throws Throwable {  
  22.         long starttime = System.currentTimeMillis();  
  23.         System.out.println("汽车开始行驶....");  
  24.         method.invoke(target);  
  25.         long endtime = System.currentTimeMillis();  
  26.         System.out.println("汽车结束行驶....  汽车行驶时间:"   
  27.                 + (endtime - starttime) + "毫秒!");  
  28.         return null;  
  29.     }  
  30.   
  31. }  
[java]  view plain  copy
  1. public class Test {  
  2.   
  3.     /** 
  4.      * JDK动态代理测试类 
  5.      */  
  6.     public static void main(String[] args) {  
  7.         Car car = new Car();  
  8.         InvocationHandler h = new TimeHandler(car);  
  9.         Class<?> cls = car.getClass();  
  10.         /** 
  11.          * loader  类加载器 
  12.          * interfaces  实现接口 
  13.          * h InvocationHandler 
  14.          */  
  15.         Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),  
  16.                                                 cls.getInterfaces(), h);  
  17.         m.move();  
  18.     }  
  19.   
  20. }  

运行结果如下:



四、JDK动态代理的简易实现

动态代理的实现思路

实现功能:通过Proxy的newProxyInstance返回代理对象

(1)声明一段源码(动态产生代理)

(2)编译源码(JDK Compiler API),产生新的类(代理类)

(3)将这个类load到内存当中,产生一个新的对象(代理对象)

(4)return代理对象

[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. public interface Moveable {  
  4.     void move();  
  5. }  
[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class Car implements Moveable {  
  6.   
  7.     @Override  
  8.     public void move() {  
  9.         //实现开车  
  10.         try {  
  11.             Thread.sleep(new Random().nextInt(1000));  
  12.             System.out.println("汽车行驶中....");  
  13.         } catch (InterruptedException e) {  
  14.             e.printStackTrace();  
  15.         }  
  16.     }  
  17.   
  18. }  
[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. import org.apache.commons.io.FileUtils;  
  4.   
  5. import javax.tools.JavaCompiler;  
  6. import javax.tools.JavaCompiler.CompilationTask;  
  7. import javax.tools.StandardJavaFileManager;  
  8. import javax.tools.ToolProvider;  
  9. import java.io.File;  
  10. import java.lang.reflect.Constructor;  
  11. import java.lang.reflect.Method;  
  12.   
  13. public class Proxy {  
  14.   
  15.     @SuppressWarnings("unchecked")  
  16.     public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{  
  17.         String rt = "\n";  
  18.         String rx = "    ";  
  19.         String methodStr = "";  
  20.         for(Method m : infce.getMethods()){  
  21.             methodStr +=  
  22.                     "@Override" + rt + rx +  
  23.                     "public void " + m.getName() + "() {" + rt + rx +  
  24.                     "    try{" + rt + rx +  
  25.                     "        Method md = " + infce.getName() + ".class.getMethod(\""+ m.getName() + "\");" + rt + rx +  
  26.                     "        h.invoke(this,md);" + rt + rx +  
  27.                     "    } catch (Exception e) { " + rt + rx +  
  28.                     "        e.printStackTrace();" + rt + rx +  
  29.                     "    }" + rt + rx +  
  30.                     "}" ;  
  31.         }  
  32.   
  33.         String str =  
  34.                 "package com.mhb.proxy;" + rt +  
  35.                 "import java.lang.reflect.Method;" + rt +  
  36.                 "import com.mhb.proxy.InvocationHandler;" +  rt+  
  37.                 "public class $Proxy0 implements " + infce.getName() + " {" + rt +  
  38.                 "    public $Proxy0(InvocationHandler h) {" + rt +  
  39.                 "        this.h = h;" + rt +  
  40.                 "    }" + rt +  
  41.                 "    private InvocationHandler h;" + rt+  
  42.                 "    " + methodStr + rt +  
  43.                 "}" ;  
  44.         //产生代理类的java文件  
  45.         String filename = System.getProperty("user.dir") +"/bin/com/mhb/proxy/$Proxy0.java";  
  46.         File file = new File(filename);  
  47.         FileUtils.writeStringToFile(file, str);  
  48.   
  49.         //编译  
  50.         //拿到编译器  
  51.         JavaCompiler complier = ToolProvider.getSystemJavaCompiler();  
  52.         //文件管理者  
  53.         StandardJavaFileManager fileMgr =  
  54.                 complier.getStandardFileManager(nullnullnull);  
  55.         //获取文件  
  56.         Iterable units = fileMgr.getJavaFileObjects(filename);  
  57.         //编译任务  
  58.         CompilationTask t = complier.getTask(null, fileMgr, nullnullnull, units);  
  59.         //进行编译  
  60.         t.call();  
  61.         fileMgr.close();  
  62.   
  63.         //load到内存  
  64.         ClassLoader cl = ClassLoader.getSystemClassLoader();  
  65.         Class c = cl.loadClass("com.mhb.proxy.$Proxy0");  
  66.   
  67.         Constructor ctr = c.getConstructor(InvocationHandler.class);  
  68.         return ctr.newInstance(h);  
  69.     }  
  70. }  
[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. public interface InvocationHandler {  
  6.   
  7.     void invoke(Object o,Method m);  
  8. }  
[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. public class TimeHandler implements InvocationHandler {  
  6.   
  7.     private Object target;  
  8.   
  9.     public TimeHandler(Object target) {  
  10.         super();  
  11.         this.target = target;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void invoke(Object o, Method m) {  
  16.   
  17.         try {  
  18.             long starttime = System.currentTimeMillis();  
  19.             System.out.println("汽车开始行驶....");  
  20.             m.invoke(target);  
  21.             long endtime = System.currentTimeMillis();  
  22.             System.out.println("汽车结束行驶....  汽车行驶时间:"  
  23.                     + (endtime - starttime) + "毫秒!");  
  24.         } catch (Exception e) {  
  25.             e.printStackTrace();  
  26.         }  
  27.     }  
  28. }  
[java]  view plain  copy
  1. package com.mhb.proxy;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * 测试类 
  7.      * @throws Exception 
  8.      */  
  9.     public static void main(String[] args) throws Exception {  
  10.         Car car = new Car();  
  11.         InvocationHandler h = new TimeHandler(car);  
  12.         Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);  
  13.         m.move();  
  14.     }  
  15. }  

IntelliJ IDEA目录结构(bin目录需要设为Resources Root):


生成的$Proxy0.java如下:

[java]  view plain  copy
  1. package com.mhb.proxy;  
  2. import java.lang.reflect.Method;  
  3. import com.mhb.proxy.InvocationHandler;  
  4. public class $Proxy0 implements com.mhb.proxy.Moveable {  
  5.     public $Proxy0(InvocationHandler h) {  
  6.         this.h = h;  
  7.     }  
  8.     private InvocationHandler h;  
  9.     @Override  
  10.     public void move() {  
  11.         try{  
  12.             Method md = com.mhb.proxy.Moveable.class.getMethod("move");  
  13.             h.invoke(this,md);  
  14.         } catch (Exception e) {   
  15.             e.printStackTrace();  
  16.         }  
  17.     }  
  18. }  


运行结果:

汽车开始行驶....汽车行驶中....汽车结束行驶....  汽车行驶时间:830毫秒!