代理模式-JDK动态代理

布满荆棘的人生 2023-05-30 10:56 84阅读 0赞

对比静态代理

静态代理:是指在程序运行前就已经定义好了目标类的代理类。代理类与目标类的代理关系在程序运行之前就确立了。

动态代理:是指程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是代理生成工具(如代理工厂类)在程序运行时由JVM根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。

概念

动态代理类类似与普通当事人与聘请的律师间的关系。律师是在“官司”发生后,才由当事人聘请的。即代理关系是在“官司”发生后才确立的。

动态代理的实现方式常用的有两种:使用JDK的Proxy,与通过CGLIB生产代理。

分析

①通过JDK的java.lang.relect.Proxy类实现动态代理,会使用其静态方法newProxyInstance(),依据目标对象、业务接口及增强逻辑三者,自动生成一个动态代理对象。

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. loader:目标类的类加载器,通过目标对象的反射可获取
  5. interfaces:目标类实现的接口数组,通过目标对象的反射可获取
  6. handler:业务增强逻辑,需要再定义
  7. InvocationHandler 是个接口,其具体介绍如下:实现了InvocationHandler 接口的类用于加强目标类的主业务逻辑。这个接口中有一个方法invoke(),具体加强的代理逻辑就是定义在该方法中的。程序调用主业务逻辑时,会自动调用invoke()方法。

②invoke()方法的介绍如下:

  1. public Object invoke(Object proxy, Method method, Object[] args)
  2. proxy:代表生成的代理对象
  3. method:代表目标方法
  4. args:代表目标方法的参数
  5. 由于该方法是由代理对象自动调用的,所以这三个参数的值不用开发人员给出。

③第二个参数为Method类对象,该类有一个方法也叫invoke(),可以调用目标类目标的方法。这两个invoke()方法,虽然同名,但无关。

  1. public Object invoke(Object obj, Object... args)
  2. obj:表示目标对象
  3. args:表示目标方法参数,就是其上一层invoke方法的第三个参数
  4. 该方法的作用是:调用执行obj对象所属类的方法,这个方法由其调用者Method对象确定。
  5. 在代码中,一般写法为method.invoke(target, args);
  6. 其中,method为上一层invoke方法的第二个参数。这样,即可调用了目标类的目标方法。

大家单看分析还不够清晰,下面看一下 代理实现与解析

(1)定义业务接口GameService,其中含有抽象方法gameStart()

  1. public interface GameService {
  2. //主业务逻辑
  3. void gameStart();
  4. }

(2)定义目标类GameServiceImpl,该类实现了业务接口。在对接口方法的实现上,只实现主业务逻辑。这个方法称为目标方法。

  1. public class GameServiceImpl implements GameService {
  2. /**
  3. * 目标方法
  4. */
  5. @Override
  6. public void gameStart() {
  7. System.out.println("这里调用Dao层等,完成gameStart()");
  8. }
  9. }

(3)定义主业务增强逻辑类JdkDynamic,该类需实现接口InvocationHandler。在该类中定义一个Object类型的成员变量,还要定义一个带参的构造器,这个参数为Object对象。目的是,将目标对象引入该类,以便通过反射调用目标方法。

当然,也可以将这个属性定义为GameService接口类型,但最好不要这样做,最好将其定义为Object。因为,这样这个主业务增强逻辑可以使用于本项目中的任何类型的目标类,而不仅仅拘泥于一个类。

  1. /*
  2. * 定义代理类,与目标类实现相同的业务接口,增强逻辑
  3. */
  4. public class JdkDynamic implements InvocationHandler {
  5. //声明业务接口对象
  6. private Object target;
  7. public JdkDynamic(){}
  8. public JdkDynamic(Object target){
  9. this.target = target;
  10. }
  11. /* 代理方法,实现对目标方法的功能增强 */
  12. @Override
  13. public Object invoke(Object proxy, Method method, Object[] args)
  14. throws Throwable {
  15. // 增加主业务逻辑代码
  16. System.out.println("对用户进行身份验证");
  17. // 无论主业务方法有无参数,有无返回值,下面的方法均可兼顾到
  18. return method.invoke(target, args);
  19. }
  20. }

(4)定义客户类Client。客户类中主要语句有三句:

A、定义目标对象。在生成代理对象时会需要目标对象对其初始化。

B、定义代理对象。需要注意的是,代理类Proxy会通过反射机制,自动实现GameService接口。代理对象需要使用目标对象对其进行初始化。

C、代理对象调用主业务方法。

  1. public static void main(String[] args) throws IOException {
  2. //定义目标对象
  3. GameService target = new GameServiceImpl();
  4. //创建代理对象,并使用目标对象初始化它
  5. GameService service = (GameService) Proxy.newProxyInstance(
  6. target.getClass().getClassLoader(), // 获取目标对象的类加载器
  7. target.getClass().getInterfaces(), // 获取目标类失效的所有接口
  8. new JdkDynamic(target)); // 增强业务逻辑
  9. //此时执行的内容,就是对目标对象增加过的内容
  10. service.gameStart();
  11. //将JDK动态代理生成的代理类,通过流的形式保存到磁盘中
  12. System.out.println(service.getClass()); // class com.sun.proxy.$Proxy0
  13. /**
  14. * 上面生成的代理对象字节码 com.sun.proxy.$Proxy0 是在内存中的
  15. * 这里将其对象写到文件中,通过反编译查看
  16. */
  17. byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{GameService.class});
  18. FileOutputStream fos = new FileOutputStream("D://code/$Proxy0.class");
  19. fos.write(bytes);
  20. fos.flush();
  21. System.out.println("文件写入成功");
  22. }

(5)将JDK动态生成的代理类,通过流的形式保存到磁盘中,使用反编译工具查看:

  1. import com.proxyDemo.jdkProxy.service.GameService;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.lang.reflect.UndeclaredThrowableException;
  6. public final class $Proxy0 extends Proxy
  7. implements GameService
  8. {
  9. private static Method m1;
  10. private static Method m3;
  11. private static Method m0;
  12. private static Method m2;
  13. public $Proxy0(InvocationHandler paramInvocationHandler)
  14. throws
  15. {
  16. super(paramInvocationHandler);
  17. }
  18. public final boolean equals(Object paramObject)
  19. throws
  20. {
  21. try
  22. {
  23. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  24. }
  25. catch (RuntimeException localRuntimeException)
  26. {
  27. throw localRuntimeException;
  28. }
  29. catch (Throwable localThrowable)
  30. {
  31. }
  32. throw new UndeclaredThrowableException(localThrowable);
  33. }
  34. public final void gameStart()
  35. throws
  36. {
  37. try
  38. {
  39. this.h.invoke(this, m3, null);
  40. return;
  41. }
  42. catch (RuntimeException localRuntimeException)
  43. {
  44. throw localRuntimeException;
  45. }
  46. catch (Throwable localThrowable)
  47. {
  48. }
  49. throw new UndeclaredThrowableException(localThrowable);
  50. }
  51. public final int hashCode()
  52. throws
  53. {
  54. try
  55. {
  56. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  57. }
  58. catch (RuntimeException localRuntimeException)
  59. {
  60. throw localRuntimeException;
  61. }
  62. catch (Throwable localThrowable)
  63. {
  64. }
  65. throw new UndeclaredThrowableException(localThrowable);
  66. }
  67. public final String toString()
  68. throws
  69. {
  70. try
  71. {
  72. return (String)this.h.invoke(this, m2, null);
  73. }
  74. catch (RuntimeException localRuntimeException)
  75. {
  76. throw localRuntimeException;
  77. }
  78. catch (Throwable localThrowable)
  79. {
  80. }
  81. throw new UndeclaredThrowableException(localThrowable);
  82. }
  83. static
  84. {
  85. try
  86. {
  87. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  88. m3 = Class.forName("com.proxyDemo.jdkProxy.service.GameService").getMethod("gameStart", new Class[0]);
  89. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  90. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  91. return;
  92. }
  93. catch (NoSuchMethodException localNoSuchMethodException)
  94. {
  95. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  96. }
  97. catch (ClassNotFoundException localClassNotFoundException)
  98. {
  99. }
  100. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  101. }
  102. }

分析JDK动态生成的代理类

  1. 1.生成的代理类继承Proxy类并实现业务接口,并生成了被代理类的全部方法,包括toStringequals等等所有方法
  2. 2.Java是单继承的,代理类实现了【需要被代理类的实现接口】以供我们可以将其强转;
  3. 3.这里生成的代理类继承自Proxy,在Proxy中有这么一段代码:protected InvocationHandler h;
  4. 这是在生成代理类的时候传入的InvocationHandler的实现,【开发的代理增强代码(实现InvocationHandler重写invoke()方法)】都写在invoke()方法中,最终代理类执行代理也是通过调用父类Proxy中的InvocationHandler接口的invoke()方法;
  5. 在调用【被代理】方法时,代理类通过调用生成的同名方法然后调用super.h.invoke()方法,最终就到了我们实现InvocationHandler时所写的invoke()增强方法;在此时,method.invoke()才真正通过反射调用了我们自身所写的方法

可以看一下这个链接下JDK是如何动态生成代理类的代理类是通过Proxy类的ProxyClassFactory工厂生成的

希望对你有帮助,祝你有一个好心情,加油!

若有错误、不全、可优化的点,欢迎纠正与补充!

发表评论

表情:
评论列表 (有 0 条评论,84人围观)

还没有评论,来说两句吧...

相关阅读

    相关 代理模式-JDK动态代理

    对比静态代理 静态代理:是指在程序运行前就已经定义好了目标类的代理类。代理类与目标类的代理关系在程序运行之前就确立了。 动态代理:是指程序在整个运行过程中根本就不存在目标类