`

深入理解java动态代理的实现机制

    博客分类:
  • java
阅读更多

今天将从以下5方面来系统的学习一下java动态代理的实现机制:

  • 什么是代理

  • 什么是静态代理

  • 什么是动态代理

  • 动态代理的实现机制

  • 动态代理的使用场景

     

1,什么是代理

 

相信大家都有购买过火车票或者机票的经历,有的人在携程买,有的在飞猪,也有的在微信上买等等,这里的携程飞猪微信也好都是受铁路部的委托代理售卖火车票,这里的携程飞猪就是代理类,铁路部就是委托类,这就是代理

 

2,什么是静态代理

 

所谓的静态代理就是在代码运行之前,代理类就已经存在,通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类,之前文章一分钟了解设计模式中的代理模式就是静态代理,具体可以点进去查阅

 

3,什么是动态代理

 

代理类在程序运行时创建的代理方式被成为动态代理,也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指令”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数,这么说比较抽象,下面我们结合一个实例来介绍一下动态代理的这个优势是怎么体现的

现在,假设我们要实现这样一个需求:我们还是以在飞猪上购买车票为例,飞猪在执行购买火车票之前需要先检查用户认证,在购买完毕后需要保存数据到自己对应的平台。首先我们来使用静态代理来实现这一需求,相关代码如下:

 

/**
* 定义一个代理购买火车票的类
*
* @author zhangqh
* @date 2018年4月29日
*/
public class ProxyChepiao implements Huochepiao {
   /**
    * 真正有权利生成火车票的地方
    */
   private Tieluju tieluju;
   @Override
   public void buyHuochepiao(String name) {
       if(tieluju == null){
           tieluju = new Tieluju();
       }
       System.out.println("买票前验证用户真实性................");
       tieluju.buyHuochepiao(name);
       System.out.println("买票后成功后数据录入自己平台.............");
   }
}

 

从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的这个需求如下

 

定义一个代理类与委托类之间的中介类:

 

 

/**
* 动态代理类
*
* @author zhangqh
* @date 2018年4月29日
*/
public class DynamicProxy implements InvocationHandler {
   /**
    * 要代理的真实对象
    */
   private Object obj;
   /**
    * 构造方式 初始化要代理的真实对象
    * @param obj
    */
   public DynamicProxy(Object obj) {
       this.obj = obj;
   }
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
       System.out.println("买票前验证用户真实性................");
       method.invoke(obj, args);
       System.out.println("买票后成功后数据录入自己平台.............");
       return null;
   }
}

 

动态生成代理类如下:

 

// 要代理的真实对象 铁路部
       Huochepiao tielubu = new Tieluju();
       // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
       InvocationHandler handler = new DynamicProxy(tielubu);
       /*
        * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
        * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
        * 第二个参数tielubu.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
        * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
        */
       Huochepiao subject = (Huochepiao)Proxy.newProxyInstance(handler.getClass().getClassLoader(), tielubu
               .getClass().getInterfaces(), handler);
       subject.buyHuochepiao("小芳");
       subject.buyHuochepiao("小明");

 

运行结果如下:

 

买票前验证用户真实性................
铁路部门为【小芳】生成一张火车票了
买票后成功后数据录入自己平台.............
买票前验证用户真实性................
铁路部门为【小明】生成一张火车票了
买票后成功后数据录入自己平台.............

 

4,动态代理的实现机制

 

以上实现了java动态代理的完整例子,下边来看看他的具体实现机制主要两个类:

 

  • InvocationHandler

  • Proxy

 

首先看看InvocationHandler接口,它就只有一个方法invoke如下:

 

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

 

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用,我们看到invoke方法一共接受三个参数,那么这三个参数分别代表什么呢?

 

proxy - 在其上调用方法的代理实例也就是代理的真实对象
method - 指的是我们所要调用真实对象的某个方法的Method对象
args - 指的是调用真实对象某个方法时接受的参数

接下来我们来看看Proxy这个类,这个类的方法就比较多了,我们这边主要看newProxyInstance这个方法如下:

 

 

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                     Class<?>[] interfaces,
                                     InvocationHandler h)
   throws IllegalArgumentException
{
   Objects.requireNonNull(h);
   final Class<?>[] intfs = interfaces.clone();
   // 获取系统安全管理器 是否有创建代理类的权限
   final SecurityManager sm = System.getSecurityManager();
   if (sm != null) {
       checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
   }
   // 生成代理类
   Class<?> cl = getProxyClass0(loader, intfs);
   try {
       if (sm != null) {
           checkNewProxyPermission(Reflection.getCallerClass(), cl);
       }
       // 调用代理的构造方法
       final Constructor<?> cons = cl.getConstructor(constructorParams);
       final InvocationHandler ih = h;
       if (!Modifier.isPublic(cl.getModifiers())) {
           AccessController.doPrivileged(new PrivilegedAction<Void>() {
               public Void run() {
                   cons.setAccessible(true);
                   return null;
               }
           });
       }
       return cons.newInstance(new Object[]{h});
   } catch (IllegalAccessException|InstantiationException e) {
       throw new InternalError(e.toString(), e);
   } catch (InvocationTargetException e) {
       Throwable t = e.getCause();
       if (t instanceof RuntimeException) {
           throw (RuntimeException) t;
       } else {
           throw new InternalError(t.toString(), t);
       }
   } catch (NoSuchMethodException e) {
       throw new InternalError(e.toString(), e);
   }
}

 

进去getProxyClass0可以看到代理类从proxyClassCache缓存中获取,代码如下:

 

private static Class<?> getProxyClass0(ClassLoader loader,
                                          Class<?>... interfaces) {
       if (interfaces.length > 65535) {
           throw new IllegalArgumentException("interface limit exceeded");
       }
       // If the proxy class defined by the given loader implementing
       // the given interfaces exists, this will simply return the cached copy;
       // otherwise, it will create the proxy class via the ProxyClassFactory
       return proxyClassCache.get(loader, interfaces);
   }

 

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
       proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

 

具体缓存是怎么生成的可以再ProxyClassFactory()进去看如下:其中最重要的就是ProxyGenerator.generateProxyClass这个生成代理类字节码

 

private static final class ProxyClassFactory implements
       BiFunction<ClassLoader, Class<?>[], Class<?>> {
   // prefix for all proxy class names
   private static final String proxyClassNamePrefix = "$Proxy";
   // next number to use for generation of unique proxy class names
   private static final AtomicLong nextUniqueNumber = new AtomicLong();
   @Override
   public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
       Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(
               interfaces.length);
       String proxyPkg = null; // package to define proxy class in
       int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
       // 中间省略了一些无关紧要的代码 .......
       // 循环遍历目标类所实现的接口
       for (Class<?> intf : interfaces) {
           int flags = intf.getModifiers();
           if (!Modifier.isPublic(flags)) {
               accessFlags = Modifier.FINAL;
               String name = intf.getName();
               int n = name.lastIndexOf('.');
               String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
               if (proxyPkg == null) {
                   proxyPkg = pkg;
               } else if (!pkg.equals(proxyPkg)) {
                   throw new IllegalArgumentException(
                           "non-public interfaces from different packages");
               }
           }
       }
       long num = nextUniqueNumber.getAndIncrement();
       String proxyName = proxyPkg + proxyClassNamePrefix + num;
       // 生成代理类的字节码
       byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,
               interfaces, accessFlags);
       try {
           // 根据代理类的字节码生成代理类的实例
           return defineClass0(loader, proxyName, proxyClassFile, 0,
                   proxyClassFile.length);
       } catch (ClassFormatError e) {
           throw new IllegalArgumentException(e.toString());
       }
   }
}

 

接下来我们再来看另外一个问题我们的动态代理中间处理类DynamicProxy中的invoke是由谁来调用的?以上我介绍过代理类的jdk底层的具体实现方法,那么现在我们通过ProxyGenerator.generateProxyClass来演示手动生成一个代码类,代码如下:

 

/**
* 手动生成动态代理的字节码文件
*
* @author zhangqh
* @date 2018年4月29日
*/
public class ProxyGeneratorUtils {
   /**
    * 把代理类的字节码写到硬盘上
    * @param path 保存路径
    */  
   public static void writeProxyClassToHardDisk(String path) {  
       // 获取代理类的字节码  
       byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy666",Tieluju.class.getInterfaces());  
       FileOutputStream out = null;  
       try {  
           out = new FileOutputStream(path);  
           out.write(classFile);  
           out.flush();  
       } catch (Exception e) {  
           e.printStackTrace();  
       } finally {  
           try {  
               out.close();  
           } catch (IOException e) {  
               e.printStackTrace();  
           }  
       }  
   }
   public static void main(String[] args) {
       writeProxyClassToHardDisk("e:/$Proxy666.class");
   }
}

 

我们的本地电脑E盘中生成一个$Proxy666.class文件,反编译如下:

 

 

import com.zhang.proxy.Huochepiao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy666 extends Proxy implements Huochepiao {
   private static Method m1;
   private static Method m3;
   private static Method m2;
   private static Method m0;
   public $Proxy666(InvocationHandler paramInvocationHandler)
   {
       super(paramInvocationHandler);
   }
   public final boolean equals(Object paramObject)
   {
       try {
           return ((Boolean) this.h.invoke(this, m1,
                   new Object[] { paramObject })).booleanValue();
       } catch (RuntimeException localRuntimeException) {
           throw localRuntimeException;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
   }
   public final void buyHuochepiao(String paramString)
   {
       try {
           this.h.invoke(this, m3, new Object[] { paramString });
           return;
       } catch (RuntimeException localRuntimeException) {
           throw localRuntimeException;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
   }
   public final String toString()
   {
       try {
           return (String) this.h.invoke(this, m2, null);
       } catch (RuntimeException localRuntimeException) {
           throw localRuntimeException;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
   }
   public final int hashCode()
   {
       try {
           return ((Integer) this.h.invoke(this, m0, null)).intValue();
       } catch (RuntimeException localRuntimeException) {
           throw localRuntimeException;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
   }
   static {
       try {
           m1 = Class.forName("java.lang.Object").getMethod("equals",
                   new Class[] { Class.forName("java.lang.Object") });
           m3 = Class.forName("com.zhang.proxy.Huochepiao").getMethod(
                   "buyHuochepiao",
                   new Class[] { Class.forName("java.lang.String") });
           m2 = Class.forName("java.lang.Object").getMethod("toString",
                   new Class[0]);
           m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                   new Class[0]);
           return;
       } catch (NoSuchMethodException localNoSuchMethodException) {
           throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
       } catch (ClassNotFoundException localClassNotFoundException) {
           throw new NoClassDefFoundError(
                   localClassNotFoundException.getMessage());
       }
   }
}

 

好了,到目前为止,谁调用的invoke已经很明显了吧,整个java的动态代理的具体机制就都讲完了,现在再用JDK动态代理的时候就不只会用而已了,就能达到了“知其然,知其所以然”了吧  

注意:jdk动态代理只能代理接口,之所以只能代理接口是因为代理类本身已经extends了Proxy,而根据java特性是不允许多重继承,但是允许实现多个接口,而cglib是支持动态的生成基于实现的代理类的,具体怎么生成的这边就不详述了,感兴趣的可以自己到网上查阅

5,动态代理的使用场景

 

上边详细的讲述了一下java动态代码的实现机制,那它到底可以用在哪些使用场景呢,如下:

a,Spring的AOP机制就是采用动态代理的机制来实现切面编程

b,我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 ,那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用

c,以及其他一下需要对方法进行增强而又不想修改源码的情况下

 

以上是今天文章的所有内容,欢迎大家吐槽酷 


 

 

更多优质文章请关注以下公众号查阅:

 

分享到:
评论

相关推荐

    基于Java动态代理和反射机制实现ORM

    本篇文章将深入探讨如何在Java环境下,结合MySQL数据库,利用动态代理和反射技术来实现简单的ORM框架。 首先,我们需要了解动态代理和反射的基本概念。动态代理是在运行时创建一个接口的实现类,这个实现类会调用...

    java动态代理demo

    本示例将带你深入理解Java动态代理的概念,并通过一个简单易懂的demo来演示其用法。 1. **Java动态代理概念** Java动态代理基于Java反射机制,可以在运行时动态地创建代理类和代理对象。与静态代理(即手动编写...

    JAVA实现动态代理的简单流程

    在深入探讨Java动态代理的实现过程之前,我们首先需要理解动态代理的基本概念及其在Java中的应用价值。动态代理,顾名思义,是在运行时动态创建代理对象的一种机制,它无需在编译期就确定代理类的具体实现,而是通过...

    Java动态代理实现数据源连接池

    Java动态代理是Java语言提供的一种机制,它允许在运行时创建一个对象的代理,这个代理对象可以在调用实际方法之前和之后添加额外的功能。在Java中,动态代理主要通过`java.lang.reflect.Proxy`类和`java.lang....

    java动态代理机制综合分析以及实现

    ### Java动态代理机制综合分析以及实现 #### 引言 Java动态代理机制为开发者提供了无需手动编写代理类的便利方式,只需指定一组接口及委托类对象即可动态获得代理类。这种代理类能够自动将所有方法调用分派到委托...

    java动态代理 经典文章(word 2007格式的)

    在Java动态代理机制中,`InvocationHandler`接口扮演着关键角色。它定义了一个`invoke()`方法,当代理对象的方法被调用时,实际上是调用了`InvocationHandler`的`invoke()`方法。这个方法接收三个参数:代理对象、...

    java动态代理反射机制

    动态代理机制允许我们在运行时创建一个实现了指定接口的代理类,这个代理类可以对方法调用进行额外的操作,如日志记录、性能监控等。Java提供了一个内置的接口`java.lang.reflect.InvocationHandler`和一个静态工厂...

    java动态代理实现数据库连接池

    ### Java动态代理实现数据库连接池 #### 背景与挑战 在开发应用程序时,数据库连接池...这种实现方式灵活且易于扩展,对于希望深入理解Java动态代理机制以及数据库连接池实现原理的开发者来说,具有较高的参考价值。

    java动态代理实现自定义连接池

    Java动态代理机制是一种强大的工具,它允许我们在运行时创建具有特定接口的新对象,这些对象的行为在编译时未被明确指定。动态代理通常用于实现AOP(面向切面编程)和拦截器模式,使得我们可以在不修改源代码的情况...

    Java动态代理案例演示代码

    Java动态代理是Java语言提供的一种强大的机制,它允许在运行时创建具有特定接口的代理类对象,这些代理类对象能够对方法调用进行拦截和增强。动态代理在很多场景下非常有用,例如AOP(面向切面编程)、事件监听、...

    java动态代理例子

    Java动态代理是Java编程中一个重要的特性,它允许我们在运行时创建代理类,这些代理类可以作为原有类的代理,从而在不修改原有类代码...理解并熟练运用这两种代理机制,能帮助我们在设计和实现复杂系统时更加游刃有余。

    深入理解java类加载机制

    总之,深入理解Java类加载机制对于Java程序员来说非常重要,它有助于提升代码质量,优化程序性能,解决运行时问题,并为高级技术如动态代理、插件化开发等提供基础。通过学习和实践,开发者可以更好地利用Java的动态...

    深入理解Java Proxy机制.doc

    以下是对Java Proxy机制的深入理解和相关知识点的详细阐述: 1. **动态代理的概念**: - 动态代理是一种编程技术,它允许在程序运行时创建代理对象,这种对象能够代表其他对象(通常称为"目标对象")执行操作。在...

    java Proxy 动态代理

    动态代理机制是Java语言提供的一种反射特性,主要用于实现AOP(面向切面编程)或者对现有接口的扩展与增强。 在Java中,动态代理主要通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口...

    Java动态代理(Spring Aop原理)

    Java动态代理是Java提供的一种在运行时创建代理对象的技术,它是通过实现InvocationHandler接口和使用Proxy类来实现的。在Spring框架中,AOP(面向切面编程)就是基于Java动态代理来完成的,用于实现横切关注点,如...

    用Java动态代理实现AOP

    在Java中,从JDK 1.2开始,提供了内置的动态代理机制来支持AOP。动态代理允许我们在运行时创建一个实现了指定接口的代理类,这个代理类可以拦截接口方法的调用,并在调用前后执行额外的逻辑。这正是AOP中的前置通知...

    Java中动态代理的介绍及实例

    ### Java中动态代理的核心知识点详解 #### 一、动态代理概览 动态代理是Java反射机制的一个重要应用,主要用于在不...掌握动态代理的原理和实现方式,对于深入理解Java编程机制和提高软件架构设计能力具有重要意义。

    java反射和动态代理实例

    首先,让我们深入理解Java反射。在Java中,`java.lang.Class`类是所有类的通用表示,它提供了获取类信息的方法。例如,`Class.forName()`用于加载类,`Class.getDeclaredMethods()`用于获取类的所有方法,包括私有...

    AOP动态代理(反射机制)

    AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,旨在提供一种方法来组织和模块化系统中的...通过阅读《AOP动态代理(反射机制)》这本书,你可以深入理解这些概念,并通过丰富的示例掌握其实现方法。

Global site tag (gtag.js) - Google Analytics