手动模拟实现动态代理--Proxy

本文详细介绍了如何手动模拟实现Java动态代理,包括FLClassLoader、FLInvocationHandler和FLProxy的实现,通过动态生成源代码、输出到磁盘、编译为.class文件并加载到JVM,最终返回字节码重组后的代理对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

承接 上一篇博客,
https://blog.youkuaiyun.com/yangsnow_rain_wind/article/details/79291256
接着分析动态代理的深层实现

当对象被动态代理之后, 原生对象被代理为一个新对象, 类型为class com.sun.proxy.$Proxy0,
这里写图片描述

我们把生成的动态类的代码保存到本地磁盘,

//以下代码把动态类保存到本地磁盘,
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{jdkPorxyUserDao.getClass()});
FileOutputStream outputStream = new FileOutputStream("e://jdkProxy.class");
outputStream.write(bytes);
outputStream.close();

反编译本地.class文件,源码如下:

public final class $Proxy0 extends Proxy implements Proxy0 {
   private static Method m1;
   private static Method m2;
.....省略多余代码
   private static Method m12;

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

   public final void save() throws  {
       try {
           super.h.invoke(this, m3, (Object[])null);
       } catch (RuntimeException | Error var2) {
           throw var2;
       } catch (Throwable var3) {
           throw new UndeclaredThrowableException(var3);
       }
   }
//判断是否为代理对象
   public final boolean isProxyClass(Class var1) throws  {
       try {
           return (Boolean)super.h.invoke(this, m4, new Object[]{var1});
       } catch (RuntimeException | Error var3) {
           throw var3;
       } catch (Throwable var4) {
           throw new UndeclaredThrowableException(var4);
       }
   }

//....................
//省略掉多余重写
//...................

   static {
       try {
           m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
           m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", Class.forName("java.lang.Object"));
           m2 = Class.forName("java.lang.Object").getMethod("toString");
           m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
           m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass");
           m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll");
           m0 = Class.forName("java.lang.Object").getMethod("hashCode");
           m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait");
           m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify");
           m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("save");
           m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler"));
           m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE);
           m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", Class.forName("java.lang.Class"));
           m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE, Integer.TYPE);
       } catch (NoSuchMethodException var2) {
           throw new NoSuchMethodError(var2.getMessage());
       } catch (ClassNotFoundException var3) {
           throw new NoClassDefFoundError(var3.getMessage());
       }
   }
}

从源码中我们发现Proxy.newProxyInstance方法生成的代理对象实际上实现了原对象的接口,
并重写了所有的方法,所有方法内被动态代理为InvocationHandler接口的invoke()

 return (String)super.h.invoke(this, m2, (Object[])null);

所以我们可以大胆的自己按照Proxy的方式, 自己手写一份动态代理机制. 所以我们模拟实现以下几个类

  1. FLClassLoader 模拟ClassLoader
  2. FLInvocationHandler模拟实现InvocationHandler
  3. FLProxy 模拟实现Proxy类

1. FLClassLoader 用来记载类到JVM

核心代码如下:

public class FLClassLoader extends ClassLoader {
   private String baseFilePath = "";
   private File classPathFile;

   public FLClassLoader() {
       baseFilePath = this.getClass().getResource("").getPath();
       classPathFile = new File(baseFilePath);
   }

  @Override
  protected Class<?> findClass(String name) {
      String className = this.getClass().getPackage().getName() + "." + name;
      try {
          if (baseFilePath != null) {
              File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
              if (classFile.exists()) {
                  FileInputStream io = new FileInputStream(classFile);
                  ByteArrayOutputStream out = new ByteArrayOutputStream();
                  int len = 0;
                  byte[] buff = new byte[1024];
                  while ((len = io.read(buff)) != -1) {
                      out.write(buff, 0, len);
                  }
                  return defineClass(className, out.toByteArray(), 0, out.size());
              }
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }
}

2. FLInvocationHandler 用来模拟回调动态代理方法的

public interface FLInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args);
}

3. FLProxy: 核心类, 用来实现返回动态代理类.

创建一个动态代理类,返回字节码重组后的代理对象, 主要需要以下步骤.

//1. 动态生成源代码;
//2. 源代码输出到磁盘
//3 编码源代码成.class文件
//4. 编译生成的.class文件 加载到jvm中
//5. 返回字节码重组后的代理对象

3.1 动态代理需要实现传入的接口

这里主要是模拟生成一个被动态生成的代理对象类, 类名字暂时定义为$Proxy0,
该类实现传入接口的所有方法, 并且方法内部被FLInvocationHandler .invoke重写,
当调用代理对象某个方法时, 实际上调用的是传入的FLInvocationHandler 类的invoke方法.
代码如下:

private static String generateSrc(Class<?>[] interfaces) {

//整理类引用信息
StringBuffer sb = new StringBuffer();
sb.append("package com.xx.cn.FLProxy;" + ln);
sb.append("import java.lang.reflect.Method;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("FLInvocationHandler h;" + ln);
sb.append("public $Proxy0(FLInvocationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);

//实习所有接口方法, 所有方法最终由this.h.invoke(this,m,null)来完成
for (Method m : interfaces[0].getMethods()) {
    sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
    sb.append("try{" + ln);
    sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
    sb.append("this.h.invoke(this,m,null);" + ln);
    sb.append("}catch(Throwable e){" + ln);
    sb.append("e.printStackTrace();" + ln);
    sb.append("}");
    sb.append("}");
}
sb.append("}" + ln);

return sb.toString();
}
3.2 生成的源代码输出到磁盘

将3.1生成的源码保存到本地磁盘上, 用于下一步被编译为.class文件

//1.
 String clssStr = generateSrc(interfaces);
 //2. 输出java
 String filePath = FLProxy.class.getResource("").getPath();
 System.out.println(filePath);
 File f = new File(filePath + "$Proxy0.java");
 FileWriter fw = new FileWriter(f);
 fw.write(clssStr);
 fw.flush();
 fw.close();
3.3 编码源代码成.class文件
  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
  Iterable iterable = manage.getJavaFileObjects(f);

  JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
  task.call();
  manage.close();
3.4 .class文件 加载到jvm中,
 ClassproxyClass = classLoader.findClass("$Proxy0");
 Constructor c = proxyClass.getConstructor(FLInvocationHandler.class);
3.5 返回字节码重组后的代理对象
return c.newInstance(h);

4. 自己实现接口FLInvocationHandler


public class FLProxyFactory implements FLInvocationHandler {
  private IUserDao userDao;
//保留原始被代理对象
  public FLProxyFactory(IUserDao userDao) {
      this.userDao = userDao;
  }
//返回一个动态代理对象.
  public IUserDao getProxy() {
      try {
          return (IUserDao) FLProxy.newProxyInstance(new FLClassLoader(), userDao.getClass().getInterfaces(), this);
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }

//动态代理核心方法,用来拦截原始的方法.
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) {
      Object result = null;
      System.out.println("before slef define proxy");
       result = method.invoke(userDao, args);
      System.out.println("after self define proxy");
      return result;     
  }
}

5. 测试

public class Demo
{
    public static void main(String[] args) {
        UserDao user = new UserDao();
        FLProxyFactory proxyFactory = new FLProxyFactory(user);
        IUserDao proxy= proxyFactory.getProxy();
        proxy.save();
    }
}

测试接口如下

before slef define proxy
----UserDao 输出!----
after self define proxy

手动模拟实现动态代理成功.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值