Java动态代理的实现方式

本篇文章着重分析的是jdk动态代理的实现过程,通过自己创建案例和对源码的一步步分析之后可以较为清晰的理解jdk动态代理的实现方式。

首先我们先来看一下java常用代理方式的区别:

1、静态代理VS动态代理

代理类可以增强被代理对象的方法,可分为静态代理和动态代理。

1.1 静态代理

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

1.2 动态代理

无需程序员自己声明代理类,是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。而动态代理的实现方式又可分为两种:
1、jdk原生动态代理
java原生动态代理是利用反射机制生成一个实现代理接口的代理类,在调用具体方法时调用InvokeHandler来处理,但被代理对象必须实现了接口。
2、cglib动态代理
cglib动态代理是利用asm开源包,对代理对象类的class文件进行加载,通过修改其字节码生成被代理类的子类来处理的。Spring的aop就是利用此方式实现动态代理的。

在这里插入图片描述 java源文件编译生成字节码

在这里插入图片描述动态代理生成字节码

可以看出动态代理没有走源码-编译-字节码这个步骤,而是直接生成代理类的字节码。

下面开始本文的重点,着重分析一下java原生动态代理的实现方式

2. java动态代理的实现方式

2.1 前提

动态代理类和被代理类必须继承同一个接口。动态代理只能对接口中声明的方法进行代理。

2.2 Proxy

java.lang.reflect.Proxy是所有动态代理的父类。它通过静态工厂ProxyClassFactory生成代理类,静态方法newProxyInstance()来创建代理类的实例。

2.3 InvocationHandler

每一个动态代理类都继承java.lang.reflect.Proxy,Proxy类中包含InvocationHandler这个成员变量,所以每个代理类在创建实例时都会引入一个InvocationHandler实例。通过代理实例调用方法,方法调用请求会被转发给InvocationHandler的invoke方法。

2.4 举例观现象

public interface HelloService {
    void sayHello();
}
public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("周杰伦的新歌真好听!");
    }
}
public class HelloInvocationHandler implements InvocationHandler {

    private Object target;

    public HelloInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("被代理方法执行前=======");
        Object result = method.invoke(target, args);
        System.out.println("被代理方法执行后=======");
        return result;
    }

    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();

        InvocationHandler invocationHandler = new HelloInvocationHandler(helloService);
        
        HelloService proxy = (HelloService) Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), invocationHandler);

        proxy.sayHello();
    }
}

执行结果:

被代理方法执行前=======
周杰伦的新歌真好听!
被代理方法执行后=======

2.5 源码分析

我们看上面的Main方法,可以看出来Proxy.newProxyInstance()这个方法创造出代理类实例,它承载了代理类的所有的逻辑。
我们点进去看看里面啥样儿的:
在这里插入图片描述简单分析一下上面的代码,大致流程如下:
1、复制被代理类的接口,这样代理类就有了原始类的方法信息,例如sayHello()。
2、得到代理类的类对象(具体如何构建下面说明)。
3、通过反射拿到代理类的构造函数,构造函数传入的参数为InvocationHandler。
4、通过反射传入之前我们定义的那个HelloInvocationHandler,进行构造器实例化代理对象。

所以我们明白了jdk动态代理,代理类必须实现接口,是因为跟他的实现有关系,他规定了必须要传一个接口去生成代理类。

然后如何生成代理类就落在getProxyClass0(loader, intfs)这个方法里了。

    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
        //这里从缓存的Cache中得到代理类
        return proxyClassCache.get(loader, interfaces);
    }
    
	   //从上面的注释和这里我们都可以看出代理类是从一个叫ProxyClassFactory里生成的
       private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

看这个方法里面的注释我们可以看出如果在缓存中存在则读取出来返回,如果没有则利用ProxyClassFactory生成代理类,点击进入这个静态工厂,我们截取其中的apply方法部分源码进行分析:

private static final String proxyClassNamePrefix = "$Proxy";
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 ...
 //这里定义了代理类的名字,所以你每次看到的代理类都是$Proxy开头的
 String proxyName = proxyPkg + proxyClassNamePrefix + num;
 //关键点在这里,这里生成一个代理类
 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
 proxyName, interfaces, accessFlags);
}

原来代理类是通过ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)这个方法创建的,如果继续点进去可以看到,里面是用StringBuilder拼出了这个代理类的源码,然后转成了Byte数组。

那么生成的这个代理类到底长啥样呢,上面可以看出byte[] proxyClassFile这个变量存储的就是代理类的字节码,如果用输出流将它输出出来并且进行反编译,我们可以看到它大致是这个样子:

public final class $Proxy0 extends Proxy implements HelloService {
 private static Method m1; //equals()方法
 private static Method m3; //sayHello()方法
 private static Method m2; //toString()方法
 private static Method m0; //hashCode()方法
 
 //这里就是我们之前提交的InvocationHandler这个构造方法!!
 public $Proxy0(InvocationHandler var1) throws {
 super(var1);
 }
 public final boolean equals(Object var1) throws {
 try {
 return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
 } catch (RuntimeException | Error var3) {
 throw var3;
 } catch (Throwable var4) {
 throw new UndeclaredThrowableException(var4);
 }
 }
 //这里就是代理类的sayHello()方法
 public final void sayHello() throws {
 try {
 super.h.invoke(this, m3, (Object[])null);
 } catch (RuntimeException | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 public final String toString() throws {
 try {
 return (String)super.h.invoke(this, m2, (Object[])null);
 } catch (RuntimeException | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 public final int hashCode() throws {
 try {
 return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
 } catch (RuntimeException | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 static {
 try {
 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
 m3 = Class.forName("proxy.service.HelloService").getMethod("sayHello", new Class[0]);
 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
 } catch (NoSuchMethodException var2) {
 throw new NoSuchMethodError(var2.getMessage());
 } catch (ClassNotFoundException var3) {
 throw new NoClassDefFoundError(var3.getMessage());
 }
 }
}

看到这里我们明白了吧!代理类里实现了接口里的方法,全部调用了HelloInvocationHandler的invoke()方法!这里说一下上面的super.h.invoke(this, m3, null);中super.h是什么东西?我们知道该代理类继承了Proxy类,Proxy里定义了InvocationHandler变量,也就是HelloInvocationHandler,这一点可以通过Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), invocationHandler)创造实例时传入的invocationHandler(= new HelloInvocationHandler( new HelloServiceImpl()))体现出来。

关于cglib动态代理,我们将在下一篇Spring aop避坑系列做一个简要的介绍。

参考文献:1、https://www.jianshu.com/p/95970b089360
2、https://www.cnblogs.com/qinggege/p/5288182.html
3、https://www.liaoxuefeng.com/wiki/1252599548343744/1281319432618017

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值