从源码分析JDK动态代理

简介: 从源码分析JDK动态代理

引言


动态代理非常的重要,虽然我们在日常的工作中没有非常底层的 编写过动态代理的代码,但是动态代理却起着非常重要的功能,想一下我们经常使用的框架: 日志框架、AOP等等,所以,如果我们需要看一些框架的源码的时候,还是需要非常熟悉的掌握动态代理的原理。


基本的使用这里就不在介绍了,前面的的博客已经介绍过了《java动态代理》,本篇博客主要解决下面几个问题:


1、JDK动态代理基本原理


2、如何调用到我们自己的myInvoerCationHandler中的invoke方法


3、 被代理的类位为什么要实现接口?


例子

package com.jack.springmvc.proxy.JDK;
public interface MapperI {
    void  jdbcOperate();
}
class  Mapper implements  MapperI{
    @Override
    public void jdbcOperate() {
        System.out.println("执行数据库连接操作");
    }
}
package com.jack.springmvc.proxy.JDK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInovercationHandler implements InvocationHandler {
    private Object target;
    public MyInovercationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("数据库连接前");
        //执行目标对象的目标方法
        method.invoke(target,args);
        System.out.println("数据库连接后");
        return null;
    }
}
package com.jack.springmvc.proxy.JDK;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) throws  Exception {
        Mapper mapper = new Mapper();
        MyInovercationHandler myInovercationHandler = new MyInovercationHandler(mapper);
        MapperI o = (MapperI)Proxy.newProxyInstance(Mapper.class.getClassLoader(), Mapper.class.getInterfaces(), myInovercationHandler);
        o.jdbcOperate();
        System.out.println(o.getClass());
//        ProxyClassUtils.createClassFile(MapperI.class);
    }
}

1、基本原理

20210222164616818.png


我们运行上面的测试方法,得到的是MapperI的一个代理对象,class com.sun.proxy.$Proxy0,执行o.jdbcOperate();这一句代码的基本流程,其实就是上图中的执行流程。为什么会按照和这个流程执行代码呢,在后面我们将生成的代理类的内容输出到文件中,我们打开看看这个类的内容就完全明白了。


2、源码执行流程

20210222171016419.png


3、代理类的内容


我们都知道动态代理生成的代理里我们在项目中看不到的,因为它保存中内存中,所以我们需要编写代码输出到代码内存中。我们从源代码中能看到下面代码:

    if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

最后 一个方法中已经得到了代理类的字节数组,所以我们可以模拟这个方法将生成的代理内输入到文件中。

import java.io.FileOutputStream;
public class ProxyClassUtils {
    public static void  createClassFile(Class clazz) throws  Exception{
        String className="$Proxy0";
        byte[] classFile = ProxyGenerator.generateProxyClass(
                className, new Class[]{clazz});
        String path ="C:\\project\\luban\\springmvc\\src\\main\\java\\com\\jack\\springmvc\\proxy\\JDK";
        FileOutputStream out = new FileOutputStream(path + className + ".class");
        out.write(classFile);
        out.flush();
        out.close();
    }
}

开头的代码中 ProxyClassUtils.createClassFile(MapperI.class) 就可以将内容输出到文件中。代理类内容如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.jack.springmvc.proxy.JDK.MapperI;
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 MapperI {
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;
    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});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    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);
        }
    }
    public final void jdbcOperate() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.jack.springmvc.proxy.JDK.MapperI").getMethod("jdbcOperate");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们看到了生成的代理类的内容,我们就能明白非常多的事情了


1、我们看到了生成的代理类继承了Proxy,但是java是单继承的机制,所以如果代理类和被代理类之间产生关联, 只能通过实现同一个接口的方式,这就是为什么,jdk代理要求被代理类必须实现接口。


2、我们还看到了一个核心的方法 jdbcOperate(),这个方法就是我们被代理类实现的接口中的方法,所以我们生成的代理类可以调用和被代理类同一个方法。


这个方法中有一个核心的代码:super.h.invoke(this, m3, (Object[])null);  这里面有几个参数:


1、h.invoke()中的 h 是什么呢,我们从源码中可以看到,cons.newInstance(new Object[]{h});  h就是我们传入的自己定义的 MyInovercationHandler ,看到这个参数我们也就 明白了,为什么我们调用生成的 代理类的jdbcOperate()方法会进入到我们自己定义的MyInovercationHandler 中。


2、m3 我们从代理类的末端可以看到m3 其实就是 我们接口中的方法,看到这我们也就能明白在我们自己的定义的MyInovercationHandler中通过method.invoke(target,args)可以调用目标对象的目标方法了。


分析到这我们也就充分的解答了 开头我们提出的几个问题, 重要的是我们将动态生成的代理类从内存中拿到本地自己看一看,就会明白了很多原来不是很懂的地方,这也是看源码的快乐。

目录
相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
13天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
14 0
[Java]静态代理与动态代理(基于JDK1.8)
|
24天前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
17 1
|
25天前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
19 1
|
3月前
|
Java API 开发者
Jdk动态代理为啥不能代理Class?
该文章主要介绍了JDK动态代理的原理以及为何JDK动态代理不能代理Class。
Jdk动态代理为啥不能代理Class?
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
189 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
162 0
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
30 0
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
40 0
|
5月前
|
缓存 Java Maven
JDK 动态代理
JDK 动态代理
27 0