zoukankan      html  css  js  c++  java
  • 动态代理类的实现和解析

     静态代理类

      由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了

    动态代理类:
       与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

    话不多说看代码。。

    package proxyBase;
    
    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.net.MalformedURLException;
    import java.util.Arrays;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    public class MyProxy {
        static Class<?>[] parameterTypes = { InvocationHandler.class };
        protected InvocationHandler h;
    
        protected MyProxy(InvocationHandler h) {
            this.h = h;
        }
    
        public static Object newInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
            // :产生一个代理类的字节码对象
            Class<?> c1 = null;
            try {
                // :通过getClass0方法可以得到一个代理类的字节码对象
                c1 = getClass0(loader, interfaces);
            } catch (MalformedURLException e1) {
                e1.printStackTrace();
            }
            Constructor<?> constructor = null;
            try {
                /**
                 * 调用子类$Proxy0(InvocationHandler h)构造函数,由于继承了MyProxy类,
                 * 所以又会继续调用父类的MyProxy(InvocationHandler h)构造函数给 h初始化一个值;
                 */
                constructor = c1.getDeclaredConstructor(InvocationHandler.class);
                /**
                 * 返回一个生成的代理类对象,并将InvocationHandler 的实现类对象传入进去。从而达到给h赋值的目的
                 * 
                 */
                return constructor.newInstance(h);
            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
                    | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        // :用来生成一个代理类对象
        private static Class<?> getClass0(ClassLoader loader, Class<?>[] interfaces) throws MalformedURLException {
            Class<?> forName = null;
            try {
                GennerateClass.generate(interfaces[0]);
                // :动态编译
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
                Iterable units = fileMgr.getJavaFileObjects(System.getProperty("user.dir") + "/src/"
                        + interfaces[0].getPackage().getName() + "/" + interfaces[0].getSimpleName() + "$proxy0.java");
                // :"-d", System.getProperty("user.dir")+"/bin/" 用来指定java文件编译后存放的地方
                Iterable<String> options = Arrays.asList("-d", System.getProperty("user.dir") + "/bin/");
                CompilationTask t = compiler.getTask(null, fileMgr, null, options, null, units);
                t.call();
                try {
                    fileMgr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // :得到代码自动生成代理类的实例对象
                forName = Class.forName(interfaces[0].getName() + "$proxy0");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return forName;
        }
    }

     这个GennerateClass类是用来生成代理类的Java文件的,是通过字符串拼接而成,仅供参考.

      1 package proxyBase;
      2 
      3 import java.io.BufferedWriter;
      4 import java.io.File;
      5 import java.io.FileWriter;
      6 import java.io.IOException;
      7 import java.lang.reflect.Method;
      8 
      9 public class GennerateClass {
     10     public static void generate(Class<?> clazz) {
     11         String methodStr = "";  //:方法字符串的拼接
     12         String classStr = "";    //:类名的拼接
     13         String packageStr = "";    //:导入包名的拼接
     14         String classParamStr = ""; //
     15         String staticCodeStr = "static {
    try {";//:静态代码块的拼接
     16         String member_var = "";  //:成员变量的拼接
     17         String package1 = clazz.getPackage().getName();
     18         String simpleName = clazz.getSimpleName(); //:获得简单类名
     19         String className = clazz.getName();    //:获得权限定类名
     20         //:构造函数的拼接
     21         String counstructStr = "public " + simpleName + "$proxy0(InvocationHandler h) {
    " + "        super(h);
    "
     22                 + "    }
    ";
     23         // :导包
     24         packageStr = "package " + package1 + ";
    "
     25                 + "import proxyBase.MyProxy;
     import java.lang.reflect.InvocationHandler;
    "
     26                 + "import java.lang.reflect.Method;
    ";
     27         // :构建类名
     28         classStr += "public class " + simpleName + "$proxy0 extends MyProxy implements " + simpleName + "{
    " + "";
     29         // :构建代理类的方法
     30         Method[] methods = clazz.getMethods();
     31         int i = 0;
     32         for (Method method : methods) {
     33             String paramStr = "";//:参数变量拼接
     34             int paramCount = 0; //:参数个数计数用来生成参数变量
     35             i += 1;
     36             member_var += "private static Method m" + i + ";
    ";//成员变量的拼接
     37             String tempStr = "";  //:参数列表
     38             String methodName = method.getName();// 方法名
     39             Class<?>[] parameterTypes = method.getParameterTypes();// 参数列表的class类型
     40             Class<?> returnType = method.getReturnType();// 返回值类型
     41             methodStr += "public final " + returnType.getName() + " " + methodName + "(";
     42             // :参数列表名字符串
     43             for (Class<?> type : parameterTypes) {
     44                 paramCount += 1;
     45                 tempStr += "," + type.getName() + " param" + paramCount;
     46                 paramStr += ",param" + paramCount;
     47                 classParamStr += "," + type.getName() + ".class";
     48             }
     49             //
     50             if (!paramStr.isEmpty()) {
     51                 paramStr = paramStr.substring(1);
     52             }
     53             if (!tempStr.isEmpty()) {
     54                 tempStr = tempStr.substring(1);
     55             }
     56             if (classParamStr.isEmpty()) {
     57                 classParamStr = "null";
     58             } else {
     59                 classParamStr = classParamStr.substring(1);
     60             }
     61             //:判断返回值是否时void,是则不需要return
     62             //方法的拼接
     63             if (returnType.getName().equals("void")) {
     64                 methodStr = methodStr + tempStr + ")
    {
    " + "	try{
    	this.h.invoke(this,m" + i + ",new Object[]{"
     65                         + paramStr + "});" + "} catch (Throwable e) {
    " + "            e.printStackTrace();
    "
     66                         + "        }
    }
    ";
     67             } else {
     68                 methodStr = methodStr + tempStr + ")
    {
    Object result=null;
    	try{
    	result=this.h.invoke(this,m" + i
     69                         + ",new Object[]{" + paramStr + "});" + "} catch (Throwable e) {
    "
     70                         + "            e.printStackTrace();
    " + "}
    return (" + returnType.getName() + ")result;}
    ";
     71             }
     72             // :构建静态代码块
     73             if (!classParamStr.equals("null")) {
     74                 staticCodeStr += "m" + i + " = Class.forName("" + className + "").getMethod("" + methodName
     75                         + "",new Class<?>[]{" + classParamStr + "});";
     76             } else {
     77                 staticCodeStr += "m" + i + " = Class.forName("" + className + "").getMethod("" + methodName + "");";
     78             }
     79             classParamStr = "";
     80         }
     81         //静态代码块的拼接
     82         staticCodeStr += "} catch (NoSuchMethodException e) {
    " + "            e.printStackTrace();
    "
     83                 + "        } catch (SecurityException e) {
    " + "            e.printStackTrace();
    "
     84                 + "        } catch (ClassNotFoundException e) {
    " + "            e.printStackTrace();
    " + "        }}";
     85         //总和成Java文件的内容
     86         packageStr = packageStr + classStr + member_var + counstructStr + methodStr + staticCodeStr + "
    }";
     87         //通过流写入成文件
     88         FileWriter fout = null;
     89         try {
     90             fout = new FileWriter(new File("src/" + package1 + "/" + simpleName + "$proxy0.java"));
     91         } catch (IOException e) {
     92 
     93             e.printStackTrace();
     94         }
     95         BufferedWriter out = new BufferedWriter(fout);
     96         try {
     97             out.write(packageStr);
     98         } catch (IOException e) {
     99             e.printStackTrace();
    100         }
    101         try {
    102             out.close();
    103             fout.close();
    104         } catch (IOException e) {
    105             e.printStackTrace();
    106         }
    107     }
    108 }
    View Code
    /***
     *这个类是用来制定你的代理对象,在调用方法时需要进行哪些前置处理和后置处理
     *
    */
    1
    package proxyBase; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class Hadler<T> implements InvocationHandler { 7 private T target; 8 //通过new Hadler将被代理的那个对象传入, 9 public Hadler(T target) { 10 super(); 11 this.target = target; 12 } 13 private void before() { 14 System.out.println("先吃饭"); 15 } 16 private void after() { 17 System.out.println("再睡觉"); 18 } 19 //这里根据自己的逻辑来制定相应的invoke方法,视情况而定,此处的写法只是来简单测试代理对象。 20 @Override 21 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 22 before(); 23 Object invoke = method.invoke(target,args); 24 after(); 25 return invoke; 26 } 27 }

    这里时生成后的代理类文件Person$proxy0.java

    package proxyBase;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class Hadler<T> implements InvocationHandler {
        private T target;
        //通过new Hadler将被代理的那个对象传入,
        public Hadler(T target) {
            super();
            this.target = target;
        }
        private void before() {
            System.out.println("先吃饭");
        }
        private void after() {
            System.out.println("再睡觉");
        }
        //这里根据自己的逻辑来制定相应的invoke方法,视情况而定,此处的写法只是来简单测试代理对象。
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object invoke = method.invoke(target,args);
            after();
            return invoke;
        }
    }

    接下来是写一个接口,jdk中的动态代理是针对接口代理的,而cglib是针对类进行代理的,这个接口将会被代理,分别写了四个方法来测试,无参,一参,两参,和一个有返回值

    的方法,都是用来测试GennerateClass类生成的是否正确,(目前只测试了这几种,如果大神发现有误,还望联系斧正)

    1 package proxy;
    2 
    3 
    4 public interface Person {
    5     void study2();
    6     void study2(int a);
    7     void study3(int b ,String a);
    8     int returnInt(int a);
    9 }

    接下来写一个Person的实现类,然后通过代理类来代理这个实现类对象,进行一些前置处理和后置处理。

     1 package proxy;
     2 
     3 public class Student implements Person {
     4     public void study2() {
     5         System.out.println("正在考试中");
     6     }
     7 
     8     @Override
     9     public void study2(int a) {
    10         System.out.println(a);
    11     }
    12 
    13     @Override
    14     public void study3(int b, String a) {
    15         System.out.println(a+b);
    16     }
    17 
    18     @Override
    19     public int returnInt(int a) {
    20         
    21         return a;
    22     }
    23 }

    最后就是测试和使用所写的代理类了

    第一步,创建一个Person类型的对象student,

    第二步,创建一个InvocationHandler的实现类对象,并将student传入进去,这个student会将生成实现类中的成员变量target进行赋值初始化。

    第三步,调用MyProxy中的newInstance方法来获得代理类对象(注意:newInstance 中的参数要是实现类的类类型来获得他的实现Interface接口的类类型,即Person,生成代理类的Java文件就是依据该接口生成的)

    第四步, 测试生成的代理类对象,结果如下,通过原生student和代理类对象stu调用相同方法进行对比

     1 public static void main(String[] args) {
     2         Person student = new Student();
     3         
     4         InvocationHandler h = new Hadler<Person>(student);
     5         Person stu = (Person) MyProxy.newInstance(Person.class.getClassLoader(), Student.class.getInterfaces(), h);
     6         stu.study2();
     7         System.out.println("-------");
     8         student.study2();
     9         stu.study2(4);
    10         stu.study3(4, "A");
    11         int a = stu.returnInt(2015);
    12         System.out.println(a);
    13     }

    运行结果:

    先吃饭
    正在考试中
    再睡觉
    -------
    正在考试中
    先吃饭
    4
    再睡觉
    先吃饭
    A4
    再睡觉
    先吃饭
    再睡觉
    2015
     
     
  • 相关阅读:
    项目管理--项目干系人与组织
    项目管理--项目生命周期概述
    项目管理--简介
    算法学习之冒泡排序,6174问题
    算法学习之基础题
    PHP5.3.8连接Sql Server SQLSRV30
    解决:安装SQl 2008为SQL Server代理服务提供的凭据无效
    Sublime Text2不自动打开最近的项目
    unix网络编程之简介
    算法学习之函数
  • 原文地址:https://www.cnblogs.com/Not-Leave-Regrets/p/11580534.html
Copyright © 2011-2022 走看看