JAVA JDK动态代理原理

JDK动态代理

在这里插入图片描述
  JDK动态代理主要利用了java.lang.reflect包下的类来实现运行时增强代码的功能,Java的三种代理模式一文中的例子生动说明了代理模式,如上图,可以把代理对象当做经纪人,目标对象是明星,用户是邀请明星的客户,那么用户一般只和经纪人洽谈,明星只负责演出事项,其他的事都由经纪人去完成。

使用条件

  1. 目标对象(被代理对象)需要实现接口,这一约束主要是由于Java没有多继承机制导致的;
  2. 需要自定义实现java.lang.reflect.InvocationHandler接口的处理器,处理器用来增强目标对象的功能,该接口只有一个方法:
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    该接口方法第一个参数为代理对象实例,第二个参数为代理对象的方法,第三个参数为方法的参数,
    该接口的实现类会持有一个私有的目标对象实例,然后在接口方法里使用,
  3. 每次通过代理对象调用被代理对象的接口方法与代理对象的equals/hashCode/toString时,会自动调用处理器的invoke方法;
  4. 代理对象是在运行时在内存中动态生成的,通过java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)获取,此时需要指定目标对象类加载器、其实现的接口类型和自定义的处理器,注意该方法返回的是一个Object类型,因此需要强转;

代码示例

  按照上面的使用条件,代码部分分为四部分,分别是接口类,实现类,处理器类和测试类。
  目标对象的接口类:

/**
 * 目标对象的接口类
 *
 */
public interface MyInterface {
    void first();
    
    void second();
}

  目标对象类:

public class MyImpl implements MyInterface {

    @Override
    public void first() {
        // TODO Auto-generated method stub
        System.out.println("first method is executing");
    }

    @Override
    public void second() {
        // TODO Auto-generated method stub

        System.out.println("second method is executing");
    }

}

  处理器类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKProxyHandler implements InvocationHandler {

    private MyInterface myInterface;

    public JDKProxyHandler(MyInterface myInterface) {
        this.myInterface = myInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        Object result;
        if ("first".equals(method.getName())) {
            System.out.println("before");
            result = method.invoke(myInterface, args);
            System.out.println("after");
        } else {
            System.out.println("-----------");
            result = method.invoke(myInterface, args);
            System.out.println("===========");
        }
        return result;
    }

}

  测试类:

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;

public class ProxyMain {
    public static void main(String[] args) {
        MyInterface myInterface = new MyImpl();
        JDKProxyHandler jdkProxy = new JDKProxyHandler(myInterface);
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),
                myInterface.getClass().getInterfaces(), jdkProxy);
        proxy.first();
        proxy.second();
        System.out.println("proxy.hashCode()执行结果:" + proxy.hashCode());
        System.out.println("代理对象的类:" + proxy.getClass());
        // createProxyClassFile();
    }

	// 用于将代理对象的class文件输出到指定文本
    private static void createProxyClassFile() {
        byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] { MyInterface.class });
        try (FileOutputStream out = new FileOutputStream("$Proxy0.class")) {
            out.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// console result:
// before
// first method is executing
// after
// -----------
// second method is executing
// ===========
// -----------
// ===========
// proxy.hashCode()执行结果:1252169911
// 代理对象的类:class com.sun.proxy.$Proxy0

结果说明

  这里代理类对目标类的first()方法增强了功能,即在代理对象proxy执行该方法前打印before,方法执行后打印after,可以看到输出结果的前三行符合预期;接下来调用了second()方法,也按预期打印了指定字符;最后调用了代理对象的hashCode()方法,也可以看到执行了增强的代码段。

原理分析

  jdk动态代理是通过反射等方式在代码运行过程中动态生成代理对象的class字节码,该字节码存在于内存中,为了查看这个class,我将这个class写入到了文件里(createProxyClassFile()方法即可),然后反编译这个文件,内容如下:

package $Proxy0;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import test.example.proxy.MyInterface;

public final class class
  extends Proxy
  implements MyInterface
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;
  
  public class(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 first()
  {
    try
    {
      this.h.invoke(this, m3, null);
      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 second()
  {
    try
    {
      this.h.invoke(this, m4, null);
      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("test.example.proxy.MyInterface").getMethod("first", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("test.example.proxy.MyInterface").getMethod("second", 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());
    }
  }
}

  可以看到,这个代理类先是继承了Proxy类,然后实现了被代理对象的接口,每一个jdk代理类都会先继承Proxy类,正是由于Java不支持多继承,因此被代理的类必须实现至少一个接口,这也是jdk动态代理为什么必须要求被代理类有接口的原因了。
在这里插入图片描述
  除此之外,我们可以看到这个类有一个静态代码块,里面初始化了五个方法,其中有两个是接口MyInterface的,还有三个分别是equals \ toString \ hashCode,而这个类的构造器是传入了一个InvocationHandler ,通过父类Proxy的源码,可以知道代理类有个内部变量h,代理类的含参构造器即将h赋值为客户端自定义的处理器,而在调用这个代理类的五个方法时,实际上会执行hinvoke(Object proxy, Method method, Object[] args)方法,每次调用时,都会把this传入,然后执行自定义处理器的invoke方法,这便是JDK的动态代理啦。

三游网

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值