zoukankan      html  css  js  c++  java
  • 转:jdk动态代理实现

    原文链接: jdk动态代理

    注:文章中用常用的流程实现 动态代理,流程逻辑比较清晰。文章后面对 “为什么要使用接口” 原理分析还未细看。

    jdk的动态代理为什么用接口,内部是什么原理呢?看了几篇文章貌似都没讲的清楚明白,因此来解释一下。

    先通过一个简单例子实现功能:

     1 //接口
     2 public interface SayService {
     3  
     4     void say(String name);
     5 }
     6 //实现类
     7 public class SayServiceImpl implements SayService{
     8  
     9     @Override
    10     public void say(String name) {
    11         System.out.println(name);
    12     }
    13 }

    然后再自定义一个增强类, 实现InvocationHandler接口:

     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3  
     4 public class WavingInvocationHandler  implements InvocationHandler{
     5  
     6     private Object target;
     7  
     8     public void setTarget(Object target) {
     9         this.target = target;
    10     }
    11  
    12     @Override
    13     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    14         System.out.println("方法执行之前!");
    15         Object obj = method.invoke(target, args);
    16         System.out.println("方法执行之后!");
    17         return obj;
    18     }
    19 }

    编写测试方法:

     1 import sun.misc.ProxyGenerator;
     2  
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.lang.reflect.Proxy;
     8  
     9 public class Test {
    10  
    11     public static void main(String[] args) {
    12  
    13         //接口
    14         SayService sayService = new SayServiceImpl();
    15         //织入类
    16         WavingInvocationHandler handler = new WavingInvocationHandler();
    17         handler.setTarget(sayService);
    18         //代理类--增强的对象
    19         SayService s = (SayService) Proxy.newProxyInstance(
    20                 sayService.getClass().getClassLoader(),
    21                 sayService.getClass().getInterfaces(), handler);
    22  
    23         s.say("say()");//执行代理对象完成业务
    24         /**
    25          方法执行之前!
    26          say()
    27          方法执行之后!
    28          */
    29  
    30         //将jdk中生成代理类输出到本地.Class文件,之后可以通过反编译软件打开查看
    31         createProxyClassFile("test12345",sayService.getClass().getInterfaces());
    32  
    33     }
    34  
    35     private static void createProxyClassFile(String name,Class<?> [] interfaces){
    36         byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//该方法为jdk中生成代理类的核心方法
    37         FileOutputStream out =null;
    38         try {
    39             out = new FileOutputStream(name+".class");
    40             System.out.println((new File(name)).getAbsolutePath());
    41             out.write(data);
    42         } catch (FileNotFoundException e) {
    43             e.printStackTrace();
    44         } catch (IOException e) {
    45             e.printStackTrace();
    46         }finally {
    47             if(null!=out) try {
    48                 out.close();
    49             } catch (IOException e) {
    50                 e.printStackTrace();
    51             }
    52         }
    53     }
    54     
    55 }

    好奇心强的小伙伴一定看过newProxyInstance方法看过了,里面的getProxyClass方法创建代理类:

    1 /*
    2  * Look up or generate the designated proxy class.
    3  */
    4 Class<?> cl = getProxyClass0(loader, intfs);

    具体是里面的哪个方法生成的,小伙伴们不用找半天了,慢慢debug后会发现,就是上面提到的

    1 ProxyGenerator.generateProxyClass()

    通过该方法生成的代理类如下:通过反编译查看源码,一看便知接口的作用

    JDK的动态代理是靠多态和反射来实现的,它生成的代理类需要实现你传入的接口,并通过反射来得到接口的方法对象(下文中的m3),并将此方法对象传参给增强类(上文中的WavingInvocationHandler类)的invoke方法去执行,从而实现了代理功能,故接口是jdk动态代理的核心实现方式,没有它就无法通过反射找到方法,所以这也是必须有接口的原因。不知道大家明白否

     1 public final class test12345 extends Proxy implements SayService {
     2     //1、该类实现你传入的接口并实现方法 
     3     // 2、通过构造方法传入你定义的增强类对象 
     4     // 3、通过反射该接口,得到接口里的Method对象并传参给增强类,然后执行Invoke实现功能
     5     private static Method m1;
     6     private static Method m2;
     7     private static Method m3;
     8     private static Method m0;
     9  
    10     public test12345(InvocationHandler paramInvocationHandler) {
    11         super(paramInvocationHandler);//2.此处的super指向Proxy中的构造方法并赋值(下文的h就是此处的增强类对象),
    12     }
    13  
    14     //略去无关的hashcode和equals方法
    15     public final void say(String paramString) {
    16         try {
    17             this.h.invoke(this, m3, new Object[]{paramString});//3.此处的h就是InvocationHandler对象,invoke就是你增强类里的方法,传入接口的方法和参数并执行你增强类里的invoke方法,即完成了整个操作!
    18         } catch (Error | RuntimeException localError) {
    19             throw localError;
    20         } catch (Throwable localThrowable) {
    21             throw new UndeclaredThrowableException(localThrowable);
    22         }
    23     }
    24  
    25     static {//1.静态代码块给属性赋值,初始化接口中的方法对象(主要是下面的m3)
    26         try {
    27             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
    28             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    29             m3 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")});
    30             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    31         } catch (NoSuchMethodException localNoSuchMethodException) {
    32             throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    33         } catch (ClassNotFoundException localClassNotFoundException) {
    34             throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    35         }
    36     }
    37 }


    ---------------------
    作者:锐锐
    来源:CSDN
    原文:https://blog.csdn.net/ray890206/article/details/70146029
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    [置顶] cocos2d-x 3.0游戏开发xcode5帅印博客教学 003.[HoldTail]游戏世界以及背景画面
    flex调用webservice中的datatable结果写入datagrid
    linux shell编程指南第十一章------------合并与分割2
    Java GUI使用exe4j打包exe文件
    最小生成树kruskal算法
    JQuery Datatable Ajax请求两次问题的解决
    其实我还好
    EFM32在使用IAR开发环境配置ICf文件以及指定程序存储地址空间
    hdu1151Air Raid
    UIWebview打开.txt文件中文乱码解决
  • 原文地址:https://www.cnblogs.com/mumu122GIS/p/10037971.html
Copyright © 2011-2022 走看看