【代理模式】Java的静态代理与动态代理

àì夳堔傛蜴生んèń 2022-12-29 09:12 312阅读 0赞

代理模式

现实生活中,在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。

程序中,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。通俗的来讲代理模式就是我们生活中常见的中介。

java为我们提供了基于接口的动态代理。介绍动态代理之前我们先说一下静态代理

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

1.静态代理

创建一个接口,然后创建一个被代理的类实现接口中的抽象方法,再创建一个代理类,让代理类也实现这个接口,在代理类里创建一个被代理的类,在调用被代理类的方法之前先做一下处理

接口 Subject

  1. public interface HelloWordInterface {
  2. void sayHello();
  3. }

被代理的类 Real Subject

  1. public class HelloWord implements HelloWordInterface {
  2. @Override
  3. public void sayHello() {
  4. System.out.println("hello laoniu");
  5. }
  6. }

代理类 Proxy

  1. public class ProxyHello implements HelloWordInterface {
  2. private HelloWordInterface hello = new HelloWord();
  3. @Override
  4. public void sayHello() {
  5. System.out.println("帮他掰开嘴");
  6. hello.sayHello();
  7. System.out.println("say 完了,闭嘴");
  8. }
  9. }

测试

  1. public class Main {
  2. public static void main(String[] args) {
  3. ProxyHello proxyHello = new ProxyHello();
  4. proxyHello.sayHello();
  5. }
  6. }

输出

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzg5MzU0_size_16_color_FFFFFF_t_70

上面这个例子很简单,使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐

2.动态代理

2.1.实现

动态代理是jdk为我们提供的利用反射机制创建代理对象的方式,接口、被代理类不变。改造一下代码

代理类

  1. public class ProxyHello implements InvocationHandler {
  2. private HelloWordInterface hello; //要代理的接口,被代理类要实现这个接口
  3. public ProxyHello(HelloWordInterface hello) {
  4. this.hello = hello;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. //如果调用的方法不是sayHello方法就不进行代理
  9. if (!method.getName().equals("sayHello")){
  10. return method.invoke(hello,args);
  11. }
  12. System.out.println("帮他掰开嘴");
  13. Object o = method.invoke(hello,args); //method是拦截的方法
  14. System.out.println("say 完了,闭嘴");
  15. return o; // o是方法的返回值
  16. }
  17. }

测试类

  1. public class Main {
  2. public static void main(String[] args) {
  3. HelloWordInterface helloWord = new HelloWord(); //被代理的对象创建
  4. ProxyHello proxyHello = new ProxyHello(helloWord); //把被代理的对象放入代理对象让其进行代理
  5. //生成代理对象
  6. HelloWordInterface proxyInstance =
  7. (HelloWordInterface)Proxy.newProxyInstance(proxyHello.getClass().getClassLoader(), //代理类的类加载器
  8. HelloWord.class.getInterfaces(),//要代理的接口
  9. proxyHello); //代理对象
  10. //使用动态生成的代理对象进行调用
  11. proxyInstance.sayHello();
  12. }
  13. }

测试

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzg5MzU0_size_16_color_FFFFFF_t_70 1

2.2分析

2.2.1 动态代理具体步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

2.2.2 jdk到底是怎样生成代理对象的?

通过查看Proxy.newProxyInstance()源码得知,最终调用了ProxyGenerator.generateProxyClass()来为我们生成了一个字节码文件.class

  1. public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
  2. ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
  3. final byte[] var4 = var3.generateClassFile();
  4. if (saveGeneratedFiles) {
  5. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  6. public Void run() {
  7. try {
  8. int var1 = var0.lastIndexOf(46);
  9. Path var2;
  10. if (var1 > 0) {
  11. Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
  12. Files.createDirectories(var3);
  13. var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
  14. } else {
  15. var2 = Paths.get(var0 + ".class");
  16. }
  17. Files.write(var2, var4, new OpenOption[0]);
  18. return null;
  19. } catch (IOException var4x) {
  20. throw new InternalError("I/O exception saving generated file: " + var4x);
  21. }
  22. }
  23. });
  24. }
  25. return var4;
  26. }

刨根问底,我们去看看我的代理对象到底长什么样子

改造一下测试类

  1. public class Main {
  2. public static void main(String[] args) {
  3. HelloWordInterface helloWord = new HelloWord(); //被代理的对象创建
  4. ProxyHello proxyHello = new ProxyHello(helloWord); //把被代理的对象放入代理对象让其进行代理
  5. //生成代理对象
  6. HelloWordInterface proxyInstance =
  7. (HelloWordInterface)Proxy.newProxyInstance(proxyHello.getClass().getClassLoader(), //代理类的类加载器
  8. HelloWord.class.getInterfaces(),//要代理的接口
  9. proxyHello); //代理对象
  10. //使用动态生成的代理对象进行调用
  11. proxyInstance.sayHello();
  12. //设置允许获取动态代理对象
  13. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  14. //保存动态代理对象
  15. FileOutputStream out = null;
  16. try {
  17. byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloWord.class.getInterfaces());
  18. //写入本地文件位置
  19. out = new FileOutputStream("/Users/laoniu/"+ "$Proxy0.class");
  20. out.write(classFile);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. } finally {
  24. try {
  25. if (out != null) {
  26. out.flush();
  27. out.close();
  28. }
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }

我们把文件使用反编译软件打开查看,idea自带反编译,我直接把它拉入idea

反编译后的动态代理类

  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by FernFlower decompiler)
  4. //
  5. import com.niu.demo.HelloWordInterface;
  6. import java.lang.reflect.InvocationHandler;
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Proxy;
  9. import java.lang.reflect.UndeclaredThrowableException;
  10. public final class $Proxy0 extends Proxy implements HelloWordInterface {
  11. private static Method m1;
  12. private static Method m3;
  13. private static Method m2;
  14. private static Method m0;
  15. public $Proxy0(InvocationHandler var1) throws {
  16. super(var1);
  17. }
  18. public final boolean equals(Object var1) throws {
  19. try {
  20. return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
  21. } catch (RuntimeException | Error var3) {
  22. throw var3;
  23. } catch (Throwable var4) {
  24. throw new UndeclaredThrowableException(var4);
  25. }
  26. }
  27. public final void sayHello() throws {
  28. try {
  29. super.h.invoke(this, m3, (Object[])null);
  30. } catch (RuntimeException | Error var2) {
  31. throw var2;
  32. } catch (Throwable var3) {
  33. throw new UndeclaredThrowableException(var3);
  34. }
  35. }
  36. public final String toString() throws {
  37. try {
  38. return (String)super.h.invoke(this, m2, (Object[])null);
  39. } catch (RuntimeException | Error var2) {
  40. throw var2;
  41. } catch (Throwable var3) {
  42. throw new UndeclaredThrowableException(var3);
  43. }
  44. }
  45. public final int hashCode() throws {
  46. try {
  47. return (Integer)super.h.invoke(this, m0, (Object[])null);
  48. } catch (RuntimeException | Error var2) {
  49. throw var2;
  50. } catch (Throwable var3) {
  51. throw new UndeclaredThrowableException(var3);
  52. }
  53. }
  54. static {
  55. try {
  56. m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  57. m3 = Class.forName("com.niu.demo.HelloWordInterface").getMethod("sayHello");
  58. m2 = Class.forName("java.lang.Object").getMethod("toString");
  59. m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  60. } catch (NoSuchMethodException var2) {
  61. throw new NoSuchMethodError(var2.getMessage());
  62. } catch (ClassNotFoundException var3) {
  63. throw new NoClassDefFoundError(var3.getMessage());
  64. }
  65. }
  66. }

可以看到在构造动态代理类的时候把我们的代理类传了进去,然后再调用代理类的invoke方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzg5MzU0_size_16_color_FFFFFF_t_70 2

看一下我们的代理类是不是上面说的被调用invoke方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzg5MzU0_size_16_color_FFFFFF_t_70 3

到这里基本就明白jdk的动态代理是怎样实现的了

3.总结

jdk的动态代理要求业务类必须得实现业务接口,底层是通过生成业务接口的动态代理实现类来完成功能增强的

但是如果我们想使用动态代理却没有接口那该怎么做呢?我们可以使用cglib。

cglib不需要业务类实现接口,底层是通过衍生出当前业务类的子类对象来完成功能增强

动态代理非常常用,比如spring的aop就是基于动态代理来实现的。文中若是有什么错误的地方请大家指出来

发表评论

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

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

相关阅读