简单分析动态代理实现原理

一、jdk的动态代理

这里拿jdk的动态代理来做例子,cglib的原理其实也是一样的
我们知道jdk的动态代理,必须实现接口.

1、定义接口

/**
 * 明星类
 */
public interface Mingxing {

    /**
     * 唱歌
     */
    public void sing() ;

    /**
     * 吃东西
     */
    public void eat();
}

2. 定义实现类

public class Dengziqi implements Mingxing{


    public void sing() {
        System.out.println("我在唱歌");
    }

    public void eat() {
        System.out.println("我在吃饭");
    }
}

3.测试类

public class Test {

    public static void main(String[] args) {
        // 保存生成的代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        final Dengziqi dengziqi = new Dengziqi();
        Mingxing mingxing = (Mingxing) Proxy.newProxyInstance(Dengziqi.class.getClassLoader(), Dengziqi.class.getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----之前----");
                Object invoke = method.invoke(dengziqi, args);
                System.out.println("----之后----");
                return invoke;

            }
        });

        mingxing.eat();
    }
}

4.运行结果

在这里插入图片描述
看到当我们调用吃饭方法,还输出了2句话.

二、简单分析

1.我们先打印一下这个代理类

System.out.println(mingxing.getClass());

查看结果
在这里插入图片描述

这个类你去包中找实际上是找不到到的.因为这个类是动态生成的, 也就是$这种开头. 数字代表序号,因为可能生成多个这个样到类.用这个序号去做区分.

2.将生成的类文件保存磁盘

我们其实有在上面加上了一行代码
// 保存生成的代理类的字节码文件
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);

运行以后,可以在目录下看到这个文件
在这里插入图片描述
这个文件其实就是代理类,打开这个文件,我们看下代理类的代码.

	//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.kangping.proxy.jdk.Mingxing;
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 Mingxing {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    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 void eat() 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 void sing() throws  {
        try {
            super.h.invoke(this, m4, (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("com.kangping.proxy.jdk.Mingxing").getMethod("eat");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.kangping.proxy.jdk.Mingxing").getMethod("sing");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

1.我们看到这个类首先实现了代理类的接口

 public final class $Proxy0 extends Proxy implements Mingxing 
   private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;
  1. 这些Method 是什么呢?
    我们看到代码最下面有个静态代码块
    static {
         try {
             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
             m3 = Class.forName("com.kangping.proxy.jdk.Mingxing").getMethod("eat");
             m2 = Class.forName("java.lang.Object").getMethod("toString");
             m4 = Class.forName("com.kangping.proxy.jdk.Mingxing").getMethod("sing");
             m0 = Class.forName("java.lang.Object").getMethod("hashCode");
         } catch (NoSuchMethodException var2) {
             throw new NoSuchMethodError(var2.getMessage());
         } catch (ClassNotFoundException var3) {
             throw new NoClassDefFoundError(var3.getMessage());
         }
     }
    
    

我们可以发现这些method 对象其实就是代理接口(Mingxing)里面到方法对象
其中还有3个Object中的方法.

  1. 我们看其中的一个方法
      public final void eat() throws  {
      try {
          super.h.invoke(this, m3, (Object[])null);
      } catch (RuntimeException | Error var2) {
          throw var2;
      } catch (Throwable var3) {
          throw new UndeclaredThrowableException(var3);
      }
    }
    
    

super.h.invoke(this, m3, (Object[])null);
看到这个有没有点熟悉呢?
我们看到我们之前测试类中到代码在这里插入图片描述
对对,这个 h 实际上就是InvocationHandler的实现类对象.
这个我们就很容易理解的这3个参数的含义
super.h.invoke(this, m3, (Object[])null);

proxy: 传的是this 实际上就是代理对象
method : 传的m3 实际上就是调用的方法对象
args : 方法参数,应为eat没有方法参数,这个是空

3.简单的看下proxy类

看下 创建代理类的这个方法
在这里插入图片描述

三、总结

动态代理主要分一下3个步骤

  1. 动态生成代理类字节码
  2. 通过类加载器把这个字节码加载到jvm
  3. 反射查创建对象返回

这个时候我们很容易理解,为什么创建代理对象到时候是3个参数
在这里插入图片描述
第一个参数是类加载器,用于加载代理类到字节码
第二个参数是代理实现接口,因为代理类需要实现相同到接口
第三个参数是InvocationHandler 对象. 在代理类方法中需要调用这个类中到方法

发布了17 篇原创文章 · 获赞 0 · 访问量 256

猜你喜欢

转载自blog.csdn.net/weixin_40980639/article/details/101160550