2022-10-18 JDK动态代理,以及自己实现源码级动态代理

JDK动态代理,以及自己实现源码级动态代理粗例

  • 1、关于动态代理

    1. JDK动态代理: 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface),一个则是 Proxy(Class)类,这个类和接口是实现我们动态代理所必须用到的。主要用到了InvocationHandler类的Object invoke(Object proxy, Method method, Object[] args) throws Throwable方法,参数说明:

      • proxy:指代我们所代理的那个真实对象,既动态生成的类的实例;
      • method:指代的是主题接口的某个方法的Method对象;
      • args:指代的是调用真实对象某个方法时接受的参数。

      两种代理模式的区别:

      • (1) JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
      • (2) CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。

      两种代理模式的速度比较:

      • (1)使用CGLIB实现动态代理,CGLIB底层采用ASM字节码生成框架,使用字节码技术生成动态代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLIB原理是动态生成被代理类的子类。
      • (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右
    2. 动态代理类:以示区别,我们将动态生成的类叫做动态代理类(注意代理类与动态代理类的区别:代理类用于获取目标类对象和增强代码插入,动态代理类是动态生成的类用于实例化真正执行增强代码的对象)

  • 2、使用JDK类库实现动态代理

    • 1)目标接口

      public interface UsbDirve {
          /*U盘定价卖*/
          int Sell(int count);
      }
      
    • 2)目标实现类

      public class UsbFactory implements UsbDirve{
          
          @Override
          public int Sell(int count) {
              return 0;
          }
      
      }
      
    • 3)代理类:实现InvocationHandler 接口的类

      //代理类
      public class UsbSellProxy implements InvocationHandler {
          //被代理对象引用
          private Object obj = null;
      
          public UsbSellProxy(Object obj){
              this.obj = obj;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object result = null;
              //调用目标对象方法
              Object data = method.invoke(obj, args);
              if(data != null) {
                  int price = (int)data;
                  price = price + 20;
                  result = price;
                  System.out.println("u盘多卖20块 :" + result);
              }else {
                  return null;
              }
      
              return result;
          }
      }
      
    • 4)测试类

      public class UsbTest {
      
          @Test
          public void test1() throws Exception {
              //获取目标类对象
              UsbFactory factory = new UsbFactory();
              //生成代理对象,需要目标对象参与,因为最终目标对象的方法调用交由它自己完成(包括目标方法的一些参数设定)。
              UsbSellProxy usbProxy = new UsbSellProxy(factory);
              //获取动态代理对象
              Object obj = Proxy.newProxyInstance(UsbFactory.class.getClassLoader(), 
              UsbFactory.class.getInterfaces(), usbProxy);
              UsbDirve proxy = (UsbDirve)obj;
              /*
              java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:
              如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,
              而互不影响,就需要用到java中对象的复制,如原生的clone()方法。
              com.sun.proxy java.lang.ClassCastException:类。
              $Proxy7不能强制转换为类think_6.Proxy.UsbFactory (com.sun.proxy。$Proxy7,think_6.Proxy。
              UsbFactory在加载器'app'的未命名模块中)
              */
              //通过代理对象执行
              proxy.Sell(0);
          }
          
      }
      
  • 3、自己编写源码

    (工具Idea2020.2(Java Compiler设置的版本为8,且使用本地JDK编译而不是Idea插件编译),JDK版本:1.8 注:在使用JavaCompiler 使用系统调用时要把JDK lib目录下的tools.jar 复制 到jre lib目录下)

    注意:接下来的代码展示中都包含了我在做测试代码时的包名,因为在接下来的测试中你会看到创建动态代理类时会与包名有关联。

    • 1)定义目标接口:TargetInterface

      package think_6.XxProxy;
      
      public interface TargetInterface {
          void targetMethod();
          int Sell(int price);
      }
      
    • 2)定义目标类:TargetClass

      package think_6.XxProxy;
      
      public class TargetClass implements TargetInterface{
          //目标方法1
          @Override
          public void targetMethod() {
              System.out.println("[TargetClass]:The target method.");
      
          }
          //目标方法2
          @Override
          public int Sell(int price) {
              System.out.println("[TargetClass]:The price:"+ price +".");
              return 0;
          }
      }
      

      以上两个类不会再修改,功能增强放在其他地方

    • 3)定义InvocationHandler的替代接口:CustomInvocationHandler

      package think_6.XxProxy;
      
      import java.lang.reflect.Method;
      
      public interface CustomInvocationHandler {
          //需要这个接口只是为提供一个invoke方法用于其实现类能够实现功能增强
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
      }
      
    • 4)TargetProxyClass :CustomInvocationHandler 实现类(用于生成代理类并实现目标方法的功能增强)

      package think_6.XxProxy;
      
      import java.lang.reflect.Method;
      
      public class TargetProxyClass implements CustomInvocationHandler{
          //获取被代理目标类对象(即TargetClass对象)
          private TargetInterface targetInterface = null;
      
          public TargetProxyClass() {}
      
          public TargetProxyClass(TargetInterface targetInterface) {
              this.targetInterface = targetInterface;
          }
          //在invoke方法中实现目标方法的增强
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object result = null;
              if(method != null) {
                  //利用Method类中的invoke方法执行目标类中的方法
                  Object dealData = method.invoke(targetInterface, args);
                  //在使用目标方法的基础上实现功能增强
                  System.out.println("[TargetProxyClass]: targetMethod 功能增强。");
              }
              return null;
          }
      }
      
    • 5)Proxy的替代类XxProxy(用于生成动态代理类并加载:$DynamicProxyClass)

      package think_6.XxProxy;
      
      
      import javax.tools.JavaCompiler;
      import javax.tools.ToolProvider;
      import java.io.File;
      import java.io.FileWriter;
      import java.io.IOException;
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Method;
      import java.lang.reflect.Parameter;
      
      public class XxProxy {
      
          private static String ln = "\r\n";
      
          /**
          *使用自定义加载器加载DynamicCreateCode生成的动态代理类java文件
          */
          public static Object newInstance (DynamicProxyClassLoader classLoader, Class<?>[] interfaces, CustomInvocationHandler h) throws IOException {
              //检查参数是否合法
              if (classLoader != null && interfaces != null && h != null) {
                  //获取字符串Java源码
                  String javaFileStr = DynamicCreateCode(interfaces[0]);//这里只实现了一个接口
                  String javaFilePath = XxProxy.class.getResource("").getPath() + "$DynamicProxyClass.java";
                  File javaFile = new File(javaFilePath);
                  System.out.println(javaFilePath);
      
                  FileWriter writer = new FileWriter(javaFile);
                  writer.write(javaFileStr);
                  writer.flush();
                  writer.close();
      
                  JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
                  int result = javaCompiler.run(null, null, null, javaFilePath);
                  System.out.println(result == 0 ? "编译成功" : "编译失败");
      
                  DynamicProxyClassLoader classLoader1 = new DynamicProxyClassLoader();
      
                  try {
                      Class<?> clazz = classLoader.findClass("$DynamicProxyClass");
                      Constructor instanceConstructor = clazz.getConstructor(CustomInvocationHandler.class);
                      javaFile.delete();
                      return instanceConstructor.newInstance(h);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
              return null;
          }
          
          public static String DynamicCreateCode(Class<?> intf) {
              StringBuffer javaSrc = new StringBuffer();
              Method[] methods = null;
              //获取接口内的所有抽象方法
              if(intf != null) {
                  methods = intf.getMethods();
              }
              javaSrc.append(XxProxy.class.getPackage().toString() +";" + ln);
              javaSrc.append("import java.lang.reflect.Method;" + ln);
              javaSrc.append("public class $DynamicProxyClass implements " + intf.getName() + "{" + ln);
              javaSrc.append("    CustomInvocationHandler h;" + ln);
              //这些静态类型以及下面的switch语句都是为了给重写的接口方法提供合法返回值
              javaSrc.append("    static int var1;" + ln);
              javaSrc.append("    static long var2;" + ln);
              javaSrc.append("    static short var3;" + ln);
              javaSrc.append("    static byte var4;" + ln);
              javaSrc.append("    static float var5 ;" + ln);
              javaSrc.append("    static double var6;" + ln);
              javaSrc.append("    static char var7 ;" + ln);
              javaSrc.append("    static String var8 = \"\";" + ln);
              javaSrc.append("    static Object var9 = null;" + ln);
              javaSrc.append("    static Class<?> var10 = null;" + ln);
              javaSrc.append("    static boolean var11 = true;" + ln);
              javaSrc.append("    public $DynamicProxyClass(CustomInvocationHandler h) {" + ln);
              javaSrc.append("        this.h = h;" + ln);
              javaSrc.append("    }" + ln);
              //重写所有接口方法
              for(int i = 0; i < methods.length; i++) {
                  //获取每个方法的返回值类型
                  String mehtodReturnType = methods[i].getReturnType().getName();
                  String setReturnType = "";
                  switch (mehtodReturnType){
                      case "int": setReturnType = "var1";break;
                      case "long": setReturnType = "var2";break;
                      case "short": setReturnType = "var3";break;
                      case "byte": setReturnType = "var4";break;
                      case "float": setReturnType = "var5";break;
                      case "double": setReturnType = "var6";break;
                      case "char": setReturnType = "var7";break;
                      case "String": setReturnType = "var8";break;
                      case "Object": setReturnType = "var9";break;
                      case "Class<?>": setReturnType = "var10";break;
                      case "boolean": setReturnType = "var11";break;
                      default:setReturnType="";
                  }
                  //设置每个方法的参数列表
                  String methodParamer = "";
                  //设置CustomInvocationHandler中invoke需要传递的参数
                  String mrthodParamerName = "";
                  //获取方法参数类型
                  String methodParamerType = "";
                  Parameter[] parameters = methods[i].getParameters();
                  Class<?>[] parameterTypes = methods[i].getParameterTypes();
                  for(int j = 0; j < parameters.length; j++){
                      if(j != parameters.length -1) {
                          methodParamer += parameters[j] + ",";
                      }else{
                          methodParamer += parameters[j];
                      }
                      if(j != parameters.length -1) {
                          mrthodParamerName += parameters[j].getName() + ", ";
                      }else{
                          mrthodParamerName += parameters[j].getName();
                      }
      
                  }
                  for(int j = 0; j < parameterTypes.length; j++) {
                      methodParamerType += "," + parameterTypes[j].toString() + ".class";
                  }
                  javaSrc.append("    public" + " " + mehtodReturnType + " " +
                          methods[i].getName() + "(" + methodParamer + ") {" + ln);
                  //判断方法是否有返回值
                  if(mehtodReturnType.equals("void")) {
                      javaSrc.append("        try{" + ln);
                      javaSrc.append("            Method m = " + intf.getName() + ".class.getMethod(\"" + methods[i].getName() + "\"" + methodParamerType + ");"
                              + ln);
                      javaSrc.append("            Object[] objs = new Object[]{" + mrthodParamerName + "};" + ln);
                      javaSrc.append("            this.h.invoke(this, m, objs );" + ln);
                      javaSrc.append("        }catch(Throwable e){e.printStackTrace();}" + ln);
                      javaSrc.append("    }" + ln);
                      javaSrc.append(ln);
                  }else{
                      javaSrc.append("        try{" + ln);
                      javaSrc.append("            Method m = " + intf.getName() + ".class.getMethod(\"" + methods[i].getName() + "\"" + methodParamerType + ");"
                              + ln);
                      javaSrc.append("            Object[] objs = new Object[]{" + mrthodParamerName + "};" + ln);
                      javaSrc.append("            this.h.invoke(this, m, objs );" + ln);
                      javaSrc.append("        }catch(Throwable e){e.printStackTrace();}" + ln);
                      javaSrc.append("        return " + setReturnType + ";" + ln);
                      javaSrc.append("    }" + ln);
                      javaSrc.append(ln);
                  }
              }
      
              javaSrc.append("}" + ln);
              return javaSrc.toString();
          }
      }
      
    • 6)自定义动态代理类加载器DynamicProxyClassLoder(用于加载动态代理类:$DynamicProxyClass)

      package think_6.XxProxy;
      
      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      
      public class DynamicProxyClassLoader extends ClassLoader{
      
          private File classFile = null;
          //加载器这里写的简单,没什么好说的:1.获取字节码文件;2.重写了ClassLoader类中的findClass()
          @Override
          protected Class<?> findClass(String name) throws ClassNotFoundException {
              File tempClassFile = new File(XxProxy.class.getResource("").getPath() + name + ".class");
              //获得类的全限定名
              String className = XxProxy.class.getPackage().toString().substring(8) + ".$DynamicProxyClass";
              //打印结果信息,无关紧要的一段代码
              System.out.println(XxProxy.class.getResource("").getPath() + name + ".class");
              System.out.println(className);
              
              FileInputStream in = null;
              ByteArrayOutputStream out = null;
              if(tempClassFile != null) {
                  classFile = tempClassFile;
                  try {
                      in = new FileInputStream(classFile);
                      out = new ByteArrayOutputStream();
                      int len = 0;
                      if(in != null) {
                          while((len = in.read()) != -1) {
                              out.write(len);
                          }
                      }
                      tempClassFile.delete();
                      byte[] classByteArray = out.toByteArray();
                      return defineClass(className, classByteArray, 0, classByteArray.length);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
      
              return null;
          }
      
      }
      
    • 测试类:XxProxyTest

      package think_6.XxProxy;
      
      import org.junit.Test;
      
      import java.io.IOException;
      
      public class XxProxyTest {
          @Test
          public void test1() throws IOException {
              //获取目标类对象
              TargetClass targetClass = new TargetClass();
              //获取代理类
              TargetProxyClass h = new TargetProxyClass(targetClass);
              /*获取动态代理对象(由XxProxy中的newInstance方法创建并返回使用,
              其实就是$DynamicProxyClass)*/
              Object obj = XxProxy.newInstance(new DynamicProxyClassLoader(), TargetClass.class.getInterfaces(), h);
              TargetInterface target = (TargetInterface)obj;
              target.targetMethod();
              target.Sell(20);
          }
      }
      

生成的动态代理类源码如下:

  • $DynamicProxyClass.java(由XxProxy中的newInstance()动态生成)
package think_6.XxProxy;
import java.lang.reflect.Method;
public class $DynamicProxyClass implements think_6.XxProxy.TargetInterface{
    CustomInvocationHandler h;
    static int var1;
    static long var2;
    static short var3;
    static byte var4;
    static float var5 ;
    static double var6;
    static char var7 ;
    static String var8 = "";
    static Object var9 = null;
    static Class<?> var10 = null;
    static boolean var11 = true;
    public $DynamicProxyClass(CustomInvocationHandler h) {
        this.h = h;
    }
    public void targetMethod() {
        try{
            Method m = think_6.XxProxy.TargetInterface.class.getMethod("targetMethod");
            Object[] objs = new Object[]{};
            this.h.invoke(this, m, objs );
        }catch(Throwable e){e.printStackTrace();}
    }

    public int Sell(int arg0) {
        try{
            Method m = think_6.XxProxy.TargetInterface.class.getMethod("Sell",int.class);
            Object[] objs = new Object[]{arg0};
            this.h.invoke(this, m, objs );
        }catch(Throwable e){e.printStackTrace();}
        return var1;
    }

}
  • $DynamicProxyClass.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package think_6.XxProxy;

import java.lang.reflect.Method;

public class $DynamicProxyClass implements TargetInterface {
    CustomInvocationHandler h;
    static int var1;
    static long var2;
    static short var3;
    static byte var4;
    static float var5;
    static double var6;
    static char var7;
    static String var8 = "";
    static Object var9 = null;
    static Class<?> var10 = null;
    static boolean var11 = true;

    public $DynamicProxyClass(CustomInvocationHandler var1x) {
        this.h = var1x;
    }

    public void targetMethod() {
        try {
            Method var1x = TargetInterface.class.getMethod("targetMethod");
            Object[] var2x = new Object[0];
            this.h.invoke(this, var1x, var2x);
        } catch (Throwable var3x) {
            var3x.printStackTrace();
        }

    }

    public int Sell(int var1x) {
        try {
            Method var2x = TargetInterface.class.getMethod("Sell", Integer.TYPE);
            Object[] var3x = new Object[]{var1x};
            this.h.invoke(this, var2x, var3x);
        } catch (Throwable var4x) {
            var4x.printStackTrace();
        }

        return var1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值