好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

查看更多关于JDK动态代理,代理接口没有实现类,实现动态代理方式的详细内容...

  阅读:40次