Java 代理模式(动态代理)

本是古典 何须时尚 2022-12-27 01:50 309阅读 0赞

代理模式

为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理,动态代理在Java中有着广泛的应用,最常见的可能就是Spring AOP了,还有Java注解对象获取、日志等各个方面。

由于Java动态代理与Java反射机制关系紧密,所以在阅读这篇文章之前读者需要对Java反射机制有一定的了解,如果不了解反射机制也不需要慌,这里推荐一篇文章反射机制详解,这篇文章对Java反射的各个方面都做了详细的解说,相信看完这篇文章之后就不会对Java反射机制那么陌生了。

动态代理的整个流程

  1. 创建被代理的类(委托类、真实类)(RealSubject)及其接口(RealSubjectInterface)
  2. 创建一个实现接口InvocationHandler的类(InvocationHandlerImpl),它必须实现接口的invoke方法
  3. 通过Proxy的静态方法newProxyInstance(), 创建一个代理对象(realSubjectProxy),RealSubjectInterface realSubjectProxy = (RealSubjectInterface)roxy.newProxyInstance(loader,interfaces,handler);
  4. 通过代理对象(realSubjectProxy)调用 委托类对象( realSubject)的方法

代码实现

  • 先定义一个接口(RealSubjectInterface)

    /**

    • 真实对象的实现接口:需要动态代理的接口
      */
      public interface Person {

      void Pay();
      }

  • 接着创建一个被代理的类(RealSubject)继承这个接口

    public class Student implements Person {

  1. @Override
  2. public void Pay() {
  3. Log.d("LEE", "Student.Pay: 支付50刀");
  4. }
  5. }
  • 创建一个实现接口InvocationHandler的类InvocationHandlerImpl

    /**

    • 每次生成动态代理类对象的时候都需要指定一个实现了该接口的调用处理器类
      **/
      public class StudentProxy implements InvocationHandler {

      //我们要代理的真实对象
      private Student student;
      //构造函数,为真实对象赋初值
      StudentProxy(Student student) {

      1. this.student = student;

      }

  1. //三个参数分别对应的是:代理对象,方法以及方法的参数
  2. @Override
  3. public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
  4. //在代理真实对象前可以添加额外操作
  5. Log.d("LEE", "StudentProxy(Before).Pay: 支付50刀");
  6. //当代理对象调用真实对象的方法时,其会自动跳转到代理对象关联的handler对象的invoke方法来进行调用
  7. method.invoke(student,objects);
  8. //在代理真实对象之后可以添加额外操作
  9. Log.d("LEE", "StudentProxy(After).Pay: 支付50刀");
  10. return null;
  11. }
  12. }
  • 调用代理类

    Student student = new Student();

    1. InvocationHandler handler = new StudentProxy(student);
    2. //创建一个代理对象person ,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
    3. Person person = (Person) Proxy.newProxyInstance(student.getClass().getClassLoader(),student.getClass().getInterfaces(),handler);
    4. person.Pay();

newProxyInstance:最终真正的代理类,它继承自Proxy并实现了我们定义的RealSubjectInterface 接口

  1. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  2. loader:   一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  3. interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态)
  4. h:   一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

NewProxyInstance源码(这里只挑重要的来讲解)

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException
  5. {
  6. Objects.requireNonNull(h);
  7. final Class<?>[] intfs = interfaces.clone();
  8. final SecurityManager sm = System.getSecurityManager();
  9. if (sm != null) {
  10. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  11. }
  12. //生成代理类对象
  13. Class<?> cl = getProxyClass0(loader, intfs);
  14. //使用指定的调用处理程序获取代理类的构造函数对象
  15. try {
  16. if (sm != null) {
  17. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  18. }
  19. final Constructor<?> cons = cl.getConstructor(constructorParams);
  20. final InvocationHandler ih = h;
  21. //如果Class作用域为私有,通过 setAccessible 支持访问
  22. if (!Modifier.isPublic(cl.getModifiers())) {
  23. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  24. public Void run() {
  25. cons.setAccessible(true);
  26. return null;
  27. }
  28. });
  29. }
  30. //获取Proxy Class构造函数,创建Proxy代理实例。
  31. return cons.newInstance(new Object[]{
  32. h});
  33. } catch (IllegalAccessException|InstantiationException e) {
  34. throw new InternalError(e.toString(), e);
  35. } catch (InvocationTargetException e) {
  36. Throwable t = e.getCause();
  37. if (t instanceof RuntimeException) {
  38. throw (RuntimeException) t;
  39. } else {
  40. throw new InternalError(t.toString(), t);
  41. }
  42. } catch (NoSuchMethodException e) {
  43. throw new InternalError(e.toString(), e);
  44. }
  45. }

Class<?> cl = getProxyClass0(loader, intfs);
重点在于这句话,利用getProxyClass0(loader, intfs)生成代理类Proxy的Class对象

  1. private static Class<?> getProxyClass0(ClassLoader loader,
  2. Class<?>... interfaces) {
  3. //如果接口数量大于65535,抛出非法参数错误
  4. if (interfaces.length > 65535) {
  5. throw new IllegalArgumentException("interface limit exceeded");
  6. }
  7. //如果指定接口的代理类已经存在与缓存中,则不用新创建,直接从缓存中取即可;
  8. //如果缓存中没有指定代理对象,则通过ProxyClassFactory来创建一个代理对象。
  9. return proxyClassCache.get(loader, interfaces);
  10. }

总结动态代理的原理如下:
1、根据代理类接口先得到代理类的类全限名、方法列表、异常列表
2、根据步骤1中的类全限名、方法列表、异常列表、接口列表生成class文件格式的字节流,其中方法的实现会最终调用InvoationHanlder的invoke方法
3、使用类加载器加载步骤2中的字节流,创建生成动态代理类对象
4、使用步骤3中创建生成的代理类对象

发表评论

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

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

相关阅读

    相关 代理模式java动态代理

    代理模式的作用及使用场景 使用代理模式的根本目的在于:如何在不直接操作对象的情况下,对此对象进行访问? 常用的场合包括:1)延迟加载;2)在调用实际对象的方法前后加入

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

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实