zoukankan      html  css  js  c++  java
  • jdk动态代理底层实现

    一、代理设计模式

    代理设计模式是Java常用的设计模式之一。

    特点:

    01.委托类和代理类有共同的接口或者父类;

    02.代理类负责为委托类处理消息,并将消息转发给委托类;

    03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;

    04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;

    二、静态代理与动态代理

    按照代理类创建的时机,代理类分为两种:

    01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;

    02.动态代理:在程序运行期间,通过反射创建出来的代理类;

    三、jdk动态代理

    顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。

    01.创建一个Person接口

    public interface Person {
        void eat();
        void sleep();
    }

    02.创建ZhangSan.java实现Person接口

    public class ZhangSan implements Person {
    
        public void eat() {
            System.out.println("吃饭...");
        }
    
        public void sleep() {
            System.out.println("睡觉...");
        }
    
    }

    03.创建代理类ZSProxy.java

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ZSProxy implements InvocationHandler {
        
        //被代理类对象引用
        private ZhangSan zhangSan;
        
        
        public ZSProxy(ZhangSan zhangSan) {
            super();
            this.zhangSan = zhangSan;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            //zhangSan.eat();
            Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值
            after();
            return null;
        }
        
        private void before() {
            System.out.println("前置...");
        }
        private void after() {
            System.out.println("后置...");
        }
    }

    jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。

    001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法

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

    proxy:代理类对象

    method:被代理的方法

    args:被代理方法的参数列表

    002.Proxy类

    public class Proxy implements java.io.Serializable {
            ... 
    
            public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException    
    
            ...
    }

    loader:类加载器

    interfaces:代理类实现的所有接口

    h:InvocationHandler接口的一个实例

    public class Test {
    
        public static void main(String[] args) throws Throwable {
            System.out.println("----------------------JDK动态代理----------------------------");
            //获取代理类对象
            Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), 
                    new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
            proxyInstance.eat();
    }

    通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。

    程序运行结果:

    虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??

    首先要了解一个类 ==》$Proxy0.java

    001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();

    002.执行$Proxy0类中的invoke

    我们debug运行观察:

    内存中代理类的特征:

    01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。

    代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。

    02.内存中的代理类必须持有InvocationHandler接口的实现类引用。

    整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?

      /**
         * 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
         */
        private static void createProxyClassFile() {
            byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class});
            
            try {
                FileOutputStream out = new FileOutputStream("$Proxy0.class");
                out.write(data);
                out.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    对$Proxy0.java反编译后

      1 public final class class extends Proxy
      2     implements Person
      3 {
      4 
      5     private static Method m1;
      6     private static Method m4;
      7     private static Method m2;
      8     private static Method m3;
      9     private static Method m0;
     10 
     11     public class(InvocationHandler invocationhandler) //通过构造将handler传入
     12     {
     13         super(invocationhandler);
     14     }
     15 
     16     public final boolean equals(Object obj)
     17     {
     18         try
     19         {
     20             return ((Boolean)super.h.invoke(this, m1, new Object[] {
     21                 obj
     22             })).booleanValue();
     23         }
     24         catch (Error ) { }
     25         catch (Throwable throwable)
     26         {
     27             throw new UndeclaredThrowableException(throwable);
     28         }
     29     }
     30 
     31     public final void eat()
     32     {
     33         try
     34         {
     35             super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法
     36             return;
     37         }
     38         catch (Error ) { }
     39         catch (Throwable throwable)
     40         {
     41             throw new UndeclaredThrowableException(throwable);
     42         }
     43     }
     44 
     45     public final String toString()
     46     {
     47         try
     48         {
     49             return (String)super.h.invoke(this, m2, null);
     50         }
     51         catch (Error ) { }
     52         catch (Throwable throwable)
     53         {
     54             throw new UndeclaredThrowableException(throwable);
     55         }
     56     }
     57 
     58     public final void sleep()
     59     {
     60         try
     61         {
     62             super.h.invoke(this, m3, null);
     63             return;
     64         }
     65         catch (Error ) { }
     66         catch (Throwable throwable)
     67         {
     68             throw new UndeclaredThrowableException(throwable);
     69         }
     70     }
     71 
     72     public final int hashCode()
     73     {
     74         try
     75         {
     76             return ((Integer)super.h.invoke(this, m0, null)).intValue();
     77         }
     78         catch (Error ) { }
     79         catch (Throwable throwable)
     80         {
     81             throw new UndeclaredThrowableException(throwable);
     82         }
     83     }
     84 
     85     static 
     86     {
     87         try
     88         {
     89             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
     90                 Class.forName("java.lang.Object")
     91             });
     92             m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
     93             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
     94             m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
     95             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
     96         }
     97         catch (NoSuchMethodException nosuchmethodexception)
     98         {
     99             throw new NoSuchMethodError(nosuchmethodexception.getMessage());
    100         }
    101         catch (ClassNotFoundException classnotfoundexception)
    102         {
    103             throw new NoClassDefFoundError(classnotfoundexception.getMessage());
    104         }
    105     }
    106 }

    那么就真相大白了。

    ********************************************************************************************************************************************

    以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。

    01.创建Person接口

    package cn.yzx.myProxy;
    
    public interface Person {
        void eat() throws Throwable;
        void sleep() throws Throwable;
    }

    02.创建ZhangSan实现类

    package cn.yzx.myProxy;
    
    public class ZhangSan implements Person {
    
        public void eat() {
            System.out.println("吃饭...");
        }
    
        public void sleep() {
            System.out.println("睡觉...");
        }
    }

    03.ZSProxy代理类

    package cn.yzx.myProxy;
    
    import java.lang.reflect.Method;
    
    public class ZSProxy implements MyInvocationHandler {
        //目标对象
        private Person person;
        
        public ZSProxy(Person person) {
            this.person = person;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object args) {
            before();
            try {
                person.eat();
            } catch (Throwable e) {
                e.printStackTrace();
            }
            after();
            return null;
        }
        
        private void before() {
            System.out.println("前置...");
        }
        private void after() {
            System.out.println("后置...");
        }
    
    }

    04.自定义动态代理Proxy类

      1 package cn.yzx.myProxy;
      2 
      3 import java.io.File;
      4 import java.io.FileWriter;
      5 import java.io.IOException;
      6 import java.lang.reflect.Constructor;
      7 import java.lang.reflect.InvocationTargetException;
      8 import java.lang.reflect.Method;
      9 
     10 import javax.tools.JavaCompiler;
     11 import javax.tools.JavaCompiler.CompilationTask;
     12 import javax.tools.StandardJavaFileManager;
     13 import javax.tools.ToolProvider;
     14 
     15 
     16 /**
     17  * 自定义的动态代理Proxy类
     18  * @author Xuas
     19  */
     20 public class MyProxy {
     21     static String rt = "
    ";
     22     /**
     23      * 自定义创建内存中代理实例的方法
     24      * @param loader
     25      * @param interfaces
     26      * @param handler
     27      * @return
     28      */
     29     public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) {
     30 
     31         try {
     32             Method[] methods = interfaces.getMethods();
     33             //01.使用拼凑字符串的方式将内存中的代理类拼出来
     34             String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
     35                     + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
     36                     + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
     37                     + getMethodString(methods, interfaces) + rt + "}";
     38             //02.使用IO将拼凑的代理类写入到文件中
     39             String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
     40             File file = new File(filePathName);
     41             FileWriter fw = new FileWriter(file);
     42             fw.write(proxyClass);
     43             fw.flush();
     44             fw.close();
     45             
     46             //03.编译上面生成的java文件
     47             JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
     48             StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, 
     49                     null, null);//文件管理器
     50             Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
     51             CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
     52             task.call();//编译
     53             fileManager.close();//关闭文件管理器
     54             /**
     55              * 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
     56              *         所以要把硬盘中class文件加载到JVM内存中去!
     57              */
     58             //04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
     59             MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy");
     60             
     61             //proxy0Clazz:就是内存中代理类的class对象
     62             Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象
     63             /**
     64              * public $Proxy0(MyInvocationHandler h) {..}
     65              * 要通过构造器的参数将handler对象的引用传进来
     66              */
     67             Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
     68             Object instance = constructor.newInstance(handler);//返回内存中代理类实例
     69             return instance;
     70         } catch (IOException e) {
     71             e.printStackTrace();
     72         } catch (ClassNotFoundException e) {
     73             e.printStackTrace();
     74         } catch (NoSuchMethodException e) {
     75             e.printStackTrace();
     76         } catch (SecurityException e) {
     77             e.printStackTrace();
     78         } catch (InstantiationException e) {
     79             e.printStackTrace();
     80         } catch (IllegalAccessException e) {
     81             e.printStackTrace();
     82         } catch (IllegalArgumentException e) {
     83             e.printStackTrace();
     84         } catch (InvocationTargetException e) {
     85             e.printStackTrace();
     86         }
     87         return null;
     88     }
     89 
     90     private static String getMethodString(Method[] methods, Class interfaces) {
     91 
     92         String proxyMe = "";
     93 
     94         for (Method method : methods) {
     95             proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
     96                     + interfaces.getName() + ".class.getMethod("" + method.getName() + "",new Class[]{});" + rt
     97                     + "this.h.invoke(this,md,null);" + rt + "}" + rt;
     98         }
     99 
    100         return proxyMe;
    101     }
    102 }

    05.自定义InvocationHandler接口

     1 package cn.yzx.myProxy;
     2 
     3 import java.lang.reflect.Method;
     4 
     5 /**
     6  * 自定义InvocationHandler接口
     7  * @author Xuas
     8  *
     9  */
    10 public interface MyInvocationHandler {
    11     public Object invoke(Object proxy, Method method, Object args);
    12 }

    06.自定义的类加载器

    package cn.yzx.myProxy;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class MyClassLoader extends ClassLoader {
        
        private File dir;
        
        public MyClassLoader(String path) {//要加载的文件路径
            dir = new File(path);
        }
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            
            if(null != dir) {
                File clazzFile = new File(dir, name + ".class");
                if(clazzFile.exists()) {
                    try {
                        FileInputStream input = new FileInputStream(clazzFile);
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[1024];
                        int len;
                        while((len = input.read(buffer)) != -1) {
                            baos.write(buffer, 0, len);
                        }
                        return defineClass("cn.yzx.myProxy."+name,
                                baos.toByteArray(),
                                0,
                                baos.size());//最终把输出流输出到JVM内存中
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return super.findClass(name);
        }
        
    }

    07.内存中自定义生成的 $Proxy0.java

     1 package cn.yzx.myProxy;
     2 import java.lang.reflect.Method;
     3 public class $Proxy0 implements cn.yzx.myProxy.Person{
     4 MyInvocationHandler h;
     5 public $Proxy0(MyInvocationHandler h) {
     6 this.h = h;
     7 }public void sleep() throws Throwable {
     8 Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
     9 this.h.invoke(this,md,null);
    10 }
    11 public void eat() throws Throwable {
    12 Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
    13 this.h.invoke(this,md,null);
    14 }
    15 
    16 }

    08.测试类

    public class Test {
    
        public static void main(String[] args) throws Throwable {
            System.out.println("----------------------自定义动态代理----------------------------");
            cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(), 
                    cn.yzx.myProxy.Person.class, 
                    new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
            person2.eat();
        }
    }

    程序运行结果:

    ************************************************************************************************************************************************

    总结:

    通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。

    如果是实现共同父类的情况,不能使用jdk动态代理。可以使用cglib动态代理。

    既然写到这了,那我就再简单介绍下cglib动态代理吧 -。 -

    cglib动态代理(接口和父类的情况都可行)

    两个关键因素:

    01.MethodInterceptor接口中的interceptor方法。

    public Object intercept(Object obj,
                  Method method,Object[] args,MethodProxy proxy) throws Throwable;

    interceptor是所有拦截器执行的方法,类似于jdk动态代理中的invoke

    02.Enhancer类

    public void setSuperclass(Class superclass) {
                      if (superclass != null && superclass.isInterface()) {
                          setInterfaces(new Class[]{ superclass });
                      } else if (superclass != null && superclass.equals(Object.class)) {
                          // affects choice of ClassLoader
                          this.superclass = null;
                      } else {
                          this.superclass = superclass;
                      }
      }

    设置委托类和代理类的公共接口或者公共父类。

    代理类执行完毕后,通知委托类:

    public void setCallback(final Callback callback) {
              setCallbacks(new Callback[]{ callback });
    }

    在Enhancer的父类AbstractClassGenerator中有一个方法 作用是创建需要的代理类

    protected Object create(Object key)

    写个例子:

    01.创建父类Animal

    package cn.yzx.cglib;
    
    public abstract class Animal {
        abstract String eat();
    }

    02.实现类Cat

    package cn.yzx.cglib;
    
    public class Cat extends Animal{
        public String eat() {
            System.out.println("cat吃饭");
            return "haha";
        }
    }

    03.代理类

    package cn.yzx.cglib;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import org.junit.Test;
    
    import java.lang.reflect.Method;
    
    public class CglibProxy implements MethodInterceptor {
    
        private Enhancer enhancer = new Enhancer();
    
        /**
         * 创建代理类对象
         *         参数:委托类Class对象
         *         返回值:代理类对象
         */
        public Object createProxyObject(Class<?> clazz){
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        /**
         * 代理类执行委托类方法
         */
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("前置");
            Object result = methodProxy.invokeSuper(o,args);
            System.out.println("后置");
            System.out.println(result);
            return result;
        }
    
        /**
         * 测试方法
         */
    
        @Test
        public void test(){
            CglibProxy proxy = new CglibProxy();
            Animal cat = (Animal) proxy.createProxyObject(Cat.class);
            cat.eat();
        }
    }

    测试结果:

    ok...

    就酱吧

  • 相关阅读:
    password
    bzoj 1458: 士兵占领
    国家集训队2011 happiness
    cogs 2051. 王者之剑
    uva 10779 Collectors Problem
    [Jxoi2012]奇怪的道路
    天神下凡
    藏宝图
    黑红树
    愤怒的小鸟
  • 原文地址:https://www.cnblogs.com/9513-/p/8432276.html
Copyright © 2011-2022 走看看