JDK动态代理详解(动态代理类源码解析)

简介
JDK动态代理就是在程序运行时,运用反射机制动态创建代理类,实现对目标类代码的增强。动态代理类的字节码在程序运行时由Java反射机制动态生成,不需要去手动开发。相比较静态代理,它更灵活。更容易扩展,代码
开发工作量更小,更容易维护。动态代理又分为JDK动态代理和CGLIB动态代理,一个通过反射生成代理类,一个通过asm开源包,修改字节码生成子类。区别在于JDK只能代理接口,所以有需要代理的类,必须实现了接口才行
,而CGLIB可以代理类,sping会自动切换两个动态代理。现在我们着重讲下JDK动态代理,它生成的匿名类是什么样的,为什么能实现代理?

JDK动态代理示例

/**
* 人员工作接口
*/
public interface WorkInterface {
    void goToWork();

    void goOffWork();
}
/**
* 工程师类
*/
public class Engineer implements WorkInterface {
    @Override
    public void goToWork() {
        System.out.print("工程师某某开始上班了\n");
    }

    @Override
    public void goOffWork() {
        System.out.print("工程师某某下班了\n");
    }
}
/**
* InvocationHandler
*/
public class WorkInvocationHandler implements InvocationHandler {
    private Object obj;

    public WorkInvocationHandler(Object obj) {
        this.obj = obj;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print("进入了代理\n");
        Object result = method.invoke(obj, args);
        System.out.print("时间为:" + new Date() + "\n\n");
        return result;
    }
}
/**
* 动态代理Main方法
*
* @param args
*/
public static void main(String[] args) {
    //工程师类,Engineer是我们的代理对象
    WorkInterface engineer = new Engineer();
    //创建一个InvocationHandler,与代理对象关联
    InvocationHandler invocationHandler = new WorkInvocationHandler(engineer);
    Class<?> engineerClass = engineer.getClass();
    //新建一个代理类来代理engineer(这里就是动态生成代理类,通过实现相同接口,
    //达到在调用目标类之前先进入InvocationHandler,然后在通过method.invoke()方法调用原有的目标类中方法)
    /**
     * Proxy 代理类中 newProxyInstance方法参数说明
     * loader: 一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
     * interfaces: 一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,
     *             那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
     * h: 一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
     */
    WorkInterface engineerProxy = (WorkInterface) Proxy.newProxyInstance(engineerClass.getClassLoader(), engineerClass.getInterfaces(), invocationHandler);
    //调用工程师类中的方法
    engineerProxy.goToWork();
    engineerProxy.goOffWork();

    //将我们生成的代理类engineerProxy中相关信息打印出来
    String methodList = "";
    for (Method m : engineerProxy.getClass().getDeclaredMethods()) {
        methodList += m.getName() + "  ";
    }
    System.out.print("engineerProxy中的方法: " + methodList + "\n");
    System.out.print("engineerProxy的父类: " + engineerProxy.getClass().getSuperclass() + "\n");
    String interfaces = "";
    for (Class<?> i : engineerProxy.getClass().getInterfaces()) {
        interfaces += i.getName() + "  ";
    }
    System.out.print("engineerProxy中实现的接口: " + interfaces + "\n");
}

运行结果:
在这里插入图片描述

进入了代理
工程师某某开始上班了
时间为:Tue Jun 25 20:37:16 CST 2019

进入了代理
工程师某某下班了
时间为:Tue Jun 25 20:37:16 CST 2019

engineerProxy中的方法: equals  toString  hashCode  goToWork  goOffWork  
engineerProxy的父类: class java.lang.reflect.Proxy
engineerProxy中实现的接口: dynamicproxy.JDKProxy.WorkInterface  

JDK动态代理源码分析
JDK动态代理最重要的两个东西,一个Proxy,一个InvocationHandler,
通过

engineerProxy的父类: class java.lang.reflect.Proxy

我们就可以看出,生成的代理类是继承Proxy类的,InvocationHandler是与被代理类(目标类)相关联的,也是在InvocationHandler中通过实现invoke方法,调用目标类中的方法。Proxy与InvocationHandler又有啥关系那?
首先最最重要的还是生成,怎么生成的代理类的?可以看到是这段代码

//新建一个代理类来代理engineer(这里就是动态生成代理类,通过实现相同接口,
//达到在调用目标类之前先进入InvocationHandler,然后在通过method.invoke()方法调用原有的目标类中方法)
/**
* Proxy 代理类中 newProxyInstance方法参数说明
* loader: 一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
* interfaces: 一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,
*             那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
* h: 一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
*/
WorkInterface engineerProxy = (WorkInterface) Proxy.newProxyInstance(engineerClass.getClassLoader(), engineerClass.getInterfaces(), invocationHandler);

Proxy.newProxyInstance 是靠Proxy类的中的newProxyInstance方法,
这是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;
                //判断代理类是否是Public的,不是public的则取消构造器验证权限限制
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    //设置在使用构造器的时候不执行权限检查
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
          /*
         * 创建代理对象,这里为什么用newInstance可以查下new和newInstance的区别,还有这里实例化的时候把h带进去了,
         * h是InvocationHandler,可以看生成的代理类中构造函数是什么样的就知道为什么传h了
         */
        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的代码,如何生成代理类的。

/**
* 生成代理类
*/
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
        //接口数量不能超过65535
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    //判断给定的代理类是否在缓存中存在,如果存在则直接返回副本,否则通过ProxyClassFactory创建
    return proxyClassCache.get(loader, interfaces);
}

proxyClassCache.get(loader, interfaces)源码就不继续往下看了,有兴趣的可以深入,我们看下当缓存中没有当前代理类的时候,是通过ProxyClassFactory来生成的,我们来看下ProxyClassFactory的源码

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    //代理类的前缀,所以为什么jdk动态代理生成的类都是$Proxy开头的
    private static final String proxyClassNamePrefix = "$Proxy";

    // 生成一个数字,保证唯一性,用于代理类的名称
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        //遍历接口
        for (Class<?> intf : interfaces) {
            /*
             * 验证类加载器中是否有当前接口intf的对象
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * 验证是否是接口
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * 验证接口不是重复的
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // 定义代理类的包
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;    //访问标志

        /*
         * 判断是否有非Public的接口,这样包名就使用非公共接口的包名,否则使用公共默认包名
         */
        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");
                }
            }
        }

        //当不存在非公共接口时,使用默认包名 com.sun.proxy
        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * 选择要生成的代理类的名称.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 生成指定的代理类文件
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            //返回代理类class对象
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

好了,我们知道了怎么生成的代理类文件,下面我们可以看看代理类文件里面都有什么代码。

JDK动态代理代理类文件查看
在执行生成代理类代码之前,加上

//配置系统属性为true,代理类生成时将自动写入磁盘
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

在这里插入图片描述
这样代码运行后,就会在项目目录下,创建出代理类文件。
在这里插入图片描述
我们对$Proxy0.class 这个文件进行反编译,看看生成的文件源码是什么样的。

package com.sun.proxy;

import dynamicproxy.JDKProxy.WorkInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/*
* 首先第一眼我们就看见了,我们生成的类是继承 Proxy的,所以为什么JDK动态代理不能代理类了,只能代理接口,不能多继承啊。
* 其次看到实现接口WorkInterface,所以在下面的代码里,我们看到了goToWork,goOffWork两个方法。
*/
public final class $Proxy0 extends Proxy
  implements WorkInterface
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;

  /*
   * 这里我们看到,$Proxy0的构造函数是有形参的,而且参数是InvocationHandler,这就解释了上面在实例化代理类时,为啥传入h这个参数了 return cons.newInstance(new Object[]{h});
   */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws
  {
        /*
        * 这里是直接了父类Proxy的构造函数,所以在goToWork方法里我们看到,this.h.invoke(this, m3, null);这里就是调用了WorkInvocationHandler中的invoke的方法。
    * 然后invoke方法通过反射调用了我们的接口WorkInterface中的goToWork方法,最终调用了Engineer类中的实现方法goToWork。
        */
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws
  {
    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 goToWork()
    throws
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

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

  public final void goOffWork()
    throws
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws
  {
    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("dynamicproxy.JDKProxy.WorkInterface").getMethod("goToWork", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("dynamicproxy.JDKProxy.WorkInterface").getMethod("goOffWork", 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());
  }
}
  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值