JDK * ,代理接口没有实现类,实现 * 方式

作者:DayFight_DayUp 时间:2021-12-21 11:55:22 

JDK * ,代理接口没有实现类,实现 *

JDK代理,代理的是接口,那么笔者想一想,既然代理的是接口,那如果没有实现类怎么办,能不能代理。答案是可以的,Mybatis就是这样的。

Mybatis使用JDK * 来实现Mapper接口,事先保存好Mapper接口,和接口声明的方法,返回值,参数类型,然后代理类的方法调用的时候使用MapperMethod这个事先放入方法缓存里的对象来真实调用功能。

笔者极度简化了一下代码:

被代理的接口:


public interface Subject2 {
   String selectById();
}

这个接口可以看成是Mapper接口

代理对象:


public class SubjectProxy2<T> implements InvocationHandler {
   private Class<T> proxyInterface;
   //这里可以维护一个缓存,存这个接口的方法抽象的对象    
   SubjectProxy2(Class<T> proxyInterface){
       this.proxyInterface = proxyInterface;
   }

@Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       if (method.getName().equals("selectById")){
           //String result = (String) method.invoke(proxyInterface,args);
           //这里可以得到方法抽象对象来调用真的的查询方法
           System.out.println("selectById调用成功");
       }
       return null;
   }
   public T getProxy(){
       return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(),new Class[]{proxyInterface},this);
   }
}

这个代理类使用了泛型,说明这个代理类可以代理所有的mapper接口。

那么接下来测试一下:


public class ProxyTest2 {
   public static void main(String[] args) {
       SubjectProxy2<Subject2> subjectProxy2 = new SubjectProxy2(Subject2.class);
       Subject2 subject2 = subjectProxy2.getProxy();
       subject2.selectById();
   }
}

结果不言而喻。肯定会有相应的输出

没有看mybatis源码的时候,我以为 * 一定要要有实现类才能代理,但是看了优秀的顶级大牛的源码之后,我才发现,原来还可以这样。

jdk * 为什么要接口

jdk的 * 为什么用接口,内部是什么原理呢?看了几篇文章貌似都没讲的清楚明白,因此来解释一下。

先通过一个简单例子实现功能:


//接口
public interface SayService {
void say(String name);
}
//实现类
public class SayServiceImpl implements SayService{
@Override
public void say(String name) {
 System.out.println(name);
}
}

然后再自定义一个增强类, 实现InvocationHandler接口:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WavingInvocationHandler  implements InvocationHandler{
private Object target;
public void setTarget(Object target) {
 this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("方法执行之前!");
 Object obj = method.invoke(target, args);
 System.out.println("方法执行之后!");
 return obj;
}
}

编写测试方法:


import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;

public class Test {
public static void main(String[] args) {
 //接口
 SayService sayService = new SayServiceImpl();
 //织入类
 WavingInvocationHandler handler = new WavingInvocationHandler();
 handler.setTarget(sayService);
 //代理类--增强的对象
 SayService s = (SayService) Proxy.newProxyInstance(
   sayService.getClass().getClassLoader(),
   sayService.getClass().getInterfaces(), handler);

s.say("say()");//执行代理对象完成业务
 /**
  方法执行之前!
  say()
  方法执行之后!
  */

//将jdk中生成代理类输出到本地.Class文件,之后可以通过反编译软件打开查看
 createProxyClassFile("test12345",sayService.getClass().getInterfaces());
}

private static void createProxyClassFile(String name,Class<?> [] interfaces){
 byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//该方法为jdk中生成代理类的核心方法
 FileOutputStream out =null;
 try {
  out = new FileOutputStream(name+".class");
  System.out.println((new File(name)).getAbsolutePath());
  out.write(data);
 } catch (FileNotFoundException e) {
  e.printStackTrace();
 } catch (IOException e) {
  e.printStackTrace();
 }finally {
  if(null!=out) try {
   out.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}
}

好奇心强的小伙伴一定看过newProxyInstance方法看过了,

里面的getProxyClass方法创建代理类:


/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);

具体是里面的哪个方法生成的,小伙伴们不用找半天了,慢慢debug后会发现,就是上面提到的


ProxyGenerator.generateProxyClass()

通过该方法生成的代理类如下:

通过反编译查看源码,一看便知接口的作用

JDK的 * 是靠多态和反射来实现的,它生成的代理类需要实现你传入的接口,并通过反射来得到接口的方法对象(下文中的m3),并将此方法对象传参给增强类(上文中的WavingInvocationHandler类)的invoke方法去执行,从而实现了代理功能,故接口是jdk * 的核心实现方式,没有它就无法通过反射找到方法,所以这也是必须有接口的原因。不知道大家明白否


public final class test12345 extends Proxy implements SayService {
   //1、该类实现你传入的接口并实现方法
   // 2、通过构造方法传入你定义的增强类对象
   // 3、通过反射该接口,得到接口里的Method对象并传参给增强类,然后执行Invoke实现功能
   private static Method m1;
   private static Method m2;
   private static Method m3;
   private static Method m0;

public test12345(InvocationHandler paramInvocationHandler) {
       super(paramInvocationHandler);//2.此处的super指向Proxy中的构造方法并赋值(下文的h就是此处的增强类对象),
   }

//略去无关的hashcode和equals方法
   public final void say(String paramString) {
       try {
           this.h.invoke(this, m3, new Object[]{paramString});//3.此处的h就是InvocationHandler对象,invoke就是你增强类里的方法,传入接口的方法和参数并执行你增强类里的invoke方法,即完成了整个操作!
       } catch (Error | RuntimeException localError) {
           throw localError;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
   }

static {//1.静态代码块给属性赋值,初始化接口中的方法对象(主要是下面的m3)
       try {
           m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
           m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
           m3 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")});
           m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
       } catch (NoSuchMethodException localNoSuchMethodException) {
           throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
       } catch (ClassNotFoundException localClassNotFoundException) {
           throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
       }
   }
}

来源:https://blog.csdn.net/a907691592/article/details/95354063

标签:JDK, , ,实现类
0
投稿

猜你喜欢

  • Java 在PDF中添加骑缝章示例解析

    2023-11-24 22:41:35
  • 四种引用类型在JAVA Springboot中的使用详解

    2023-11-24 03:34:38
  • C#的通用DbHelper类(支持数据连接池)示例详解

    2022-01-14 11:59:56
  • java 利用反射获取内部类静态成员变量的值操作

    2023-03-28 21:30:04
  • java多线程实现下载图片并压缩

    2023-01-17 22:28:34
  • Android编程之利用服务实现电话监听的方法

    2022-02-16 03:59:42
  • Android 自定义view仿支付宝咻一咻功能

    2023-06-01 07:42:09
  • Java Spring Dubbo三种SPI机制的区别

    2022-05-04 00:29:51
  • Java超详细讲解抽象类的原理与用法

    2022-10-31 20:51:42
  • Mybatis一对一延迟加载实现过程解析

    2022-09-07 12:45:43
  • Android多渠道打包神器ProductFlavor详解

    2021-06-11 19:48:22
  • WPF实现动画效果(七)之演示图板

    2021-08-18 20:31:53
  • Java7到Java17之Switch语句进化史示例详解

    2021-11-03 18:47:37
  • 详解JAVA高质量代码之数组与集合

    2022-03-31 16:42:07
  • java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

    2023-08-06 04:59:03
  • C#中Linq延迟查询的例子

    2022-01-15 02:43:11
  • Entity Framework主从表数据加载方式

    2022-03-10 21:44:14
  • C#实现图片加相框的方法

    2022-06-02 13:52:28
  • 使用TypeScript开发微信小程序的方法

    2023-08-30 10:42:03
  • Java实现单向链表反转

    2023-11-18 01:03:11
  • asp之家 软件编程 m.aspxhome.com