服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - JDK动态代理,代理接口没有实现类,实现动态代理方式

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

2021-12-01 13:14DayFight_DayUp Java教程

这篇文章主要介绍了JDK动态代理,代理接口没有实现类,实现动态代理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

JDK动态代理,代理接口没有实现类,实现动态代理

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

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

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

被代理的接口:

?
1
2
3
public interface Subject2 {
    String selectById();
}

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

代理对象:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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接口。

那么接下来测试一下:

?
1
2
3
4
5
6
7
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的动态代理为什么用接口,内部是什么原理呢?看了几篇文章貌似都没讲的清楚明白,因此来解释一下。

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

?
1
2
3
4
5
6
7
8
9
10
11
//接口
public interface SayService {
 void say(String name);
}
//实现类
public class SayServiceImpl implements SayService{
 @Override
 public void say(String name) {
  System.out.println(name);
 }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
 }
}

编写测试方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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方法创建代理类:

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

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

?
1
ProxyGenerator.generateProxyClass()

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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

延伸 · 阅读

精彩推荐