聊聊JDK动态代理原理

什么是代理

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。——来自百度百科

通俗地讲,就是我们生活中的中介

什么是动态代理

动态代理是在程序运行时通过反射机制操作字节码,从而动态的创建 字节码文件,进而创建代理对象。

不多说了,先上代码

  • 创建接口:
public interface OutInfo {
    void printInfo();
}
  • 创建实现类:
public class Person implements OutInfo {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void printInfo() {
        System.out.println(name + "..." + age);
    }
}
  • 编写动态代理demo
import java.lang.reflect.Proxy;

public class DynamicProxy {
    public static void main(String[] args) {
        Person person = new Person("张三", 23);
        OutInfo oi = (OutInfo) Proxy.newProxyInstance(
                Person.class.getClassLoader(),
                Person.class.getInterfaces(),
                /*
                 * 此代码基于JDK8编写,所以用到了Lambda表达式
                 */
                (proxy, method, arg) -> {
                    System.out.println("方法前处理");
                    /*
                     * 注意:invoke()的第一个参数只能写被代理对象的实例
                     *  不能写参数proxy,否则会栈溢出,原因后面讲解
                     */
                    Object ret = method.invoke(person, arg);
                    System.out.println("方法后处理");
                    return ret;
                }
        );
        oi.printInfo();
    }
}

看运行结果:

从运行结果看,代理结果是没问题的。那么,代理的过程是什么样的呢?

代理过程分析

之前说过动态代理是通过操作字节码来动态的创建代理对象的,那我们先看一下它生成的字节码文件里面有些什么东西:

通过以下代码可以获取到动态代理生成的字节码文件,但是需要独立的JRE才可以,嗯!推荐用JDK8!!!

public void saveProxyClassFile(){
    // 参数:(生成的字节吗对象的名字,被代理对象实现的接口数组)
    byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy", new Class[]{OutInfo.class});
    try(
            FileOutputStream fos =new FileOutputStream(new File("$Proxy.class"))
    ){
        fos.write(bytes);
        fos.flush();
    }catch (Exception e) {
        e.printStackTrace();
    }
}

这是通过idea自带的反编译工具翻译出来的内容:

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

    public $Proxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void printInfo() 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);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("ann.study.OutInfo").getMethod("printInfo");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

通过阅读翻译出来的代码,我们可以知道变量m3保存的是接口的方法对象,而生成的代理方法printInfo()中有这么一行代码:

super.h.invoke(this, m3, (Object[])null);

其中,h就是我们用Lambda表达式传入的匿名对象,而这个方法的作用就是调用我们重写的invoke()方法。

其实这个方法是这样子的:

public Object invoke(Object proxy, Method method, Object[] args)

把参数对应起来后,我们就会知道参数proxy的作用,以及动态代理demo中method.invoke(person, arg)中写proxy会引起栈溢出的原因:

> 递归调用引起了死循环,程序无法结束循环,所以会报栈溢出异常

通过这种机制,JDK便完成了动态代理

总结一下

  1. 动态代理是通过操作字节码文件来生成代理对象的,这样就解决了静态代理生成的代理类很多以及维护困难的问题
  2. 动态代理中有一些坑,比如说:
    1. JDK的动态代理要求被代理类必须实现接口,返回的代理对象也是该接口的实现类;如果想不实现接口,可以使用cglib
    2. 在实现InvocationHandler接口,重写incvoke()方法时,要注意method.invoke()的第一个参数要传被代理对象,不能传重写的invoke()方法的第一个参数,因为那个参数是代理对象。


这是博主第一次写博文,内容中错误的地方还请评论指正,同时也希望大家多多包涵。最后,感谢大家阅读我的博文。

原文地址:https://www.cnblogs.com/ann-zhgy/p/11610765.html

时间: 2024-08-13 17:06:50

聊聊JDK动态代理原理的相关文章

【转】JDK动态代理原理

之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了.     废话不多说了,先来看一下JDK的动态是怎么用的. Java代码   package dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.

jdk动态代理原理

http://www.cnblogs.com/MOBIN/p/5597215.html   请先查看这边博文 此文主要是在上篇博文的基础之上,宏观的理一下思路,因为之前本人看了上篇之后云里雾里(是本人技术不到位).然后研究了一会儿jdk1.8的源码之后写了此篇,不是很准确,源码上篇博文之中已经很多了,我就不加了. 1 获取代理对象,需要传入类加载器,需要被实现的接口, InvocationHandler对象(该对象中,包含了我们自己编写的接口实现类实例,也就是未被代理的业务逻辑) 调用的是 Pr

Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理

JDK动态代理的实现及原理 作者:二青 邮箱:[email protected]     微博:http://weibo.com/xtfggef 动态代理,听上去很高大上的技术,在Java里应用广泛,尤其是在Hibernate和Spring这两种框架里,在AOP,权限控制,事务管理等方面都有动态代理的实现.JDK本身有实现动态代理技术,但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功能更加强大的动态代理技术-- CG

jdk动态代理的实现原理

原文地址:http://rejoy.iteye.com/blog/1627405 之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了.     废话不多说了,先来看一下JDK的动态是怎么用的. Java代码 package dynamic.proxy; import java.lang.reflect.InvocationHandler; import

jdk动态代理与cglib代理、spring aop代理实现原理解析

原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 动态代理实现主要有2种形式,主要分为: 1.jdk动态代理: 1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下) 2)实现方式: 1. 通过实现Invocati

JDK动态代理实现原理--转载

之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了.     废话不多说了,先来看一下JDK的动态是怎么用的. Java代码   package dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.

JDK和cglib动态代理原理

本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Spring源码解析 https://blog.csdn.net/column/details/21851.html 部分代码会放在我的的Github:https://github.com/h2pl/ AOP的基础是Java动态代理,了解和使用两种动态代理能让我们更好地理解 AOP,在讲解AOP之前,让我们先来看看Java动态代理的使用方式以及底层实现原理. 转自https://www.jia

jdk动态代理实现原理总结

运行时创建一个代理对象的步骤为 生成代理类的字节码 加载字节码,创建代理对象 首先,代理对象的目的是在被调用方法的时候既能实现被代理对象的方法,又能够增加自己想要执行的方法,自然要获得被代理对象 代理类需要实现被代理对象的类的接口,因此生成的代理对象可以调用被代理对象所执行的方法.因此,创建代理类需要获得被代理对象的类的接口(反射获得) 代理类实现的接口方法由用户自定义放到一个类方法里,因此代理类需要拿到这个用户自定义的类(构造方法或者set方法) 既然要加载字节码,肯定要获得classLoad

设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网上已经有很多优质的文章,我这里就不会再过多的介绍了,这里推荐几篇优质的文章作为参考: 给女朋友讲解什么是代理模式 轻松学,Java 中的代理模式及动态代理 另外,我的 github 仓库对应目录中也有相关的基础示例代码:https://github.com/eamonzzz/java-advance