浅谈JDK9以前的Proxy.newProxyInstance

因为涉及到代理模式,以及JAVA的反射技术,预料将会收获良多,而且这对于我来说也会是一个里程碑,故写下记录;

代理模式

java.lang.reflact.Proxy故名思意就是为了代理模式而生的,而且是动态代理模式,以下是代理模式的关系图:
这里写图片描述

代理模式在很多框架和中间件中都有用到,线程池,Spring的事务、AOP等等,而所有的设计模式都是为了在相应的场景下使工程满足开闭原则,如果工程中已经有一个相关的实现类,且该实现类也正在使用,但我们仅需要在相同的接口下增减一些功能代码,又或者需要创建多个这样小改动的类,去满足新的需求,这时候使用代理模式就十分适合了;

PS:JDK的动态代理模式是针对接口进行动态代理,还有其他第三方实现是以类为基础实现动态代理的;

动态代理模式与静态代理

动态代理即是在运行后才生成代理类,而不是在编译时就生成,编译期生成代理类的是静态代理模式,静态代理模式就是完全自己实现所有公共接口,在实现里调用真实角色(类)的实现,所以代码量会多很多,而动态代理模式只需要实现需要的接口,其他接口实现都可以直接使用真实角色(类)的实现;

相关源码

Proxy类的核心就是newProxyInstance方法,使用到java的反射技术:

Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

简单用法:
* 我们将使用一个简单的例子去理解整个过程,真实生产一般不会这样用,这个例子只是为了方便理解,spring事务的代理模式十分经典,那才是广泛适用的工程代码;

  • 以下是简单的例子,两个类,一个公共接口(想着例子代码差不多,就照搬了,如果侵权了,请告诉我,我马上改一个例子):

    1. 电脑工商生产电脑(真实角色)
    2. 代理商卖电脑在成本上+1500(代理角色)
    3. Providable接口定义公共行为,只有sellComputer(double price)repairComputer(double price),真实角色、代理角色均实现Providable接口(实际上生产环境中会让真实类实现多个接口)
public static void main(String[] args) {
    //直接调用真实角色
    Providable p1 = new ComputerFactory();
    //调用卖电脑
    p1.sellComputer(3500);

    //调用修电脑方法
    p1.repairComputer(200);

    /*
    生成代理对象
     */
    Providable p2 = (Providable) Proxy.newProxyInstance(
            // 参数1:真实对象的类加载器
            p1.getClass().getClassLoader(),
            //参数2:真实对象实现的所有的接口,接口是特殊的类,使用Class[]装载多个接口
            new Class[]{Providable.class},
            //参数3: 接口,传递一个匿名内部类对象
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //proxy:代理对象
                    //method: 代理的方法对象
                    //args: 方法调用时参数
                    if (method.getName().equals("sellComputer")) {
                        //得到方法的参数
                        double price = (double) args[0];
                        //如果是卖电脑,加价1500
                        System.out.println("代理商卖电脑:" + (price + 1500));
                        return null;
                    }
                    else {
                        //如果是修电脑,调用原来的方法,不修改原来的方法
                        return method.invoke(p1,args);
                    }
                }
            }
    );
    //调用代理对象的方法
    p2.sellComputer(3500);
    p2.repairComputer(200);
}
// 输出
厂家卖电脑:3500.0
厂家修电脑:200.0
代理商卖电脑:5000.0
厂家修电脑:200.0

进入源码:

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

        /*
         * Look up or generate the designated proxy class.
         */
        // 装载真实对象的类加载器与接口,获得 $Proxy0 类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
        // 使用反射获得构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 检查真实角色的类是否是public可获取的
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                    // 不是就强行破解权限,设置为可获取
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 装载 实现了invoke()方法的InvocationHandler接口,与下面$Proxy0的构造方法一致
            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);
        }
}

最终的代理类:
$Proxy0.class(就是我们上面的Providable p2)

package com.sun.proxy;

import cn.css.proxy.Providable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements Providable
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void sellComputer(double paramDouble)
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { Double.valueOf(paramDouble) });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void repairComputer(double paramDouble)
  {
    try
    {
      this.h.invoke(this, m4, new Object[] { Double.valueOf(paramDouble) });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    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("cn.css.proxy.Providable").getMethod("sellComputer", new Class[] { Double.TYPE });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("cn.css.proxy.Providable").getMethod("repairComputer", new Class[] { Double.TYPE });
      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());
    }
  }
}
  • 可以看出,每个方法里都调用了InvocationHandler的invoke()方法,且在初始static代码块里已经生成了相关的method信息,直接注入invoke()就可以了,生成的代理类也实现了相同的接口,可以赋值给p2

如何提取$Proxy0

因为提取$Proxy0历经了很多艰辛,所以与其说这篇Blog是分析newProxyInstance,倒不如说是如何提取$Proxy0的 -_-|||;
在参考了其他大神的方法后,按以下步骤来:

  • 往main里添加代码,修改JVM参数和显示提取出来的代理类文件的路径
public static void main(String[] args) {
   // JDK10里的newProxyInstance的实现改了,不能使用这种方式获取,这种方法仅限JDK9以前
    // 保存动态代理生成的对象,路径是com/sun/proxy,注意是项目的根目录,创建com/sun/proxy/$Proxy.class,运行后$Proxy.class会有内容
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    // 输出生成的代理类的路径, 从项目根目录开始
    System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(Providable.class.getClassLoader(), Providable.class));
    //直接调用真实角色
    Providable p1 = new ComputerFactory();
    //调用卖电脑
    p1.sellComputer(3500);
    //调用修电脑方法
    p1.repairComputer(200);
    /*
    生成代理对象
     */
    Providable p2 = (Providable) Proxy.newProxyInstance(
          .......
    );
}

提取$Proxy的方法参考:

https://blog.csdn.net/yidianyidi123/article/details/79515035

后话

  • JDK10(JDK9官方发布一段时间后砍了,但我们可以从JDK源码注释中看到原newProxyInstance已经被修改,getProxyClass0方法被取消了)

1.8:

这里写图片描述

10.0:
这里写图片描述

JDK10的newProxyInstance实现:


@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);

        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
}
  • 不过通过IDEA debug 看了一下,p2的类名还是$Proxy0, 估计最终生成的代理类模板还是与原来的JDK8一致:
    这里写图片描述
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值