JDK动态代理

代理类在程序运行时创建的代理方式被称为动态代理,也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指令”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
静态代理就是在代码运行之前,代理类就已经存在,通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

先上demo,再说原理

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如接口中包含上百个方法呢?

这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的这个需求如下

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

在这里插入图片描述
在这里插入图片描述

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

原理:
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);
   }
}

在这段代码中 // 生成代理类 Class<?> cl = getProxyClass0(loader, intfs); 尤为重要,是这段代码生成了代理类,我们继续往下看

进去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);
   }

那么proxyClassCache缓存又是什么呢?继续看

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());
       }
   }
}

我们看到,代码中生成了代理类的字节码,这就是说java动态的在运行时生成了一个新的类,上面的字节码就是生成的类的字节码,我们可以同样按照上面的方式生成类字节码,下面代码可以自定义打印出类的字节码

/**
* 手动生成动态代理的字节码文件
*/
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");
   }
}

这个方法和上面代码的方法,一样,我们来看一下java动态生成的类是什么样子:

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);
       }
   }
}

看到这里恍然大悟,上面动态生成的类有buyHuochepiao() 方法,而且这个方法里调用了invoke方法,也就是我们前面定义的代理类的invoke方法,这也就是所有方法其实都是调用自定义的代理类的invoke方法的原因了;

这样,我们就可以总结出了:

要想jdk动态代理,我们就要让我们的代理类实现InvocationHandler接口,然后将要代理的对象传给我们自定义的代理类,然后用Proxy的newProxyInstance方法去获取我们的代理对象,最后就可以直接用了。

注意:jdk动态代理只能代理接口,之所以只能代理接口是因为代理类本身已经extends了Proxy(这是代理类中源码写的),而根据java特性是不允许多重继承,但是允许实现多个接口,而cglib是支持动态的生成基于实现的代理类的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值