浅谈java代理

【0】README

0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java代理机制 的基础知识;


【1】代理相关

1.1)利用代理可以在运行时创建一个实现了一组给定接口的新类, 这种功能只有在 编译时无法确定需要实现哪个接口时 才有必要使用;
1.2)假设有一个表示接口的 Class对象, 它的确切类型在编译时无法知道。 要想构造一个实现这些接口的类, 就需要使用 newInstance 方法或反射找出这个类的构造器;但是不能实例化一个接口, 需要在程序处于运行状态时定义一个新类;
1.3)解决问题的方法(代理机制): 代理机制是一种更好的解决方法。代理类可以在运行时创建全新的类, 这样代理类能够实现指定的接口,尤其是, 它具有以下方法:

  • 1.3.1)指定接口所需要的全部方法;
  • 1.3.2) Object 类中的全部方法, 例如 , toString, equals等;
  • 1.3.3)然而,不能在运行时定义这些方法的新代码。而是要提供一个 调用处理器(InvocationHandler), 该处理器是实现了 InvocationHandler 接口的类对象。在这个接口中只有一个方法: Object invoke(Object proxy, Method method, Object[] args) ; 要知道, 无论何时调用代理对象的方法, 调用处理器的invoke 方法都会被调用, 并向其传递Method对象和原始的调用参数;

1.4)要想创建一个代理对象, 需要使用Proxy类的 newProxyInstance 方法, 该方法有3个参数:

  • 1.4.1)一个类加载器:
  • 1.4.2)一个Class 对象数组: 每个元素都是需要实现的接口;
  • 1.4.3)一个调用处理器:
class TraceHandler implements InvocationHandler

InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);

1.5)但是,还有两个问题需要解决:
当然了,这两个问题的答案取决于打算使用代理机制解决什么问题。使用代理可能出于很多原因,如:

  • ex1)路由对远程服务费去的方法调用;
  • ex2)在程序运行期间, 将用户接口事件与动作关联起来;
  • ex3)为调试, 跟踪方法调用;

【2】看个荔枝(跟踪方法调用):

public class ProxyTest
{
   public static void main(String[] args)
   {
      Object[] elements = new Object[1000];

      // fill elements with proxies for the integers 1 ... 1000
      for (int i = 0; i < elements.length; i++)
      {
         Integer value = i + 1;
         InvocationHandler handler = new TraceHandler(value);
         Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);
         elements[i] = proxy;
      }

      // construct a random integer
      Integer key = new Random().nextInt(elements.length) + 1;

      // search for the key
      int result = Arrays.binarySearch(elements, key);

      // print match if found
      if (result >= 0) System.out.println(elements[result]);
   }
}

class TraceHandler implements InvocationHandler
{
   private Object target;

   public TraceHandler(Object t)
   {
      target = t;
   }

   public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
   {
      // print implicit argument
      System.out.print(target);
      // print method name
      System.out.print("." + m.getName() + "(");
      // print explicit arguments
      if (args != null)
      {
         for (int i = 0; i < args.length; i++)
         {
            System.out.print(args[i]);
            if (i < args.length - 1) System.out.print(", ");
         }
      }
      System.out.println(")");

      // invoke actual method
      return m.invoke(target, args);
   }
}

2.1)打印结果:
这里写图片描述

【2】代理类的特性

2.1)需要记住: 代理类是在程序运行过程中创建的, 然而, 一旦被创建, 就变成了常规类,与虚拟机中的任何其他类没有区别;
2.2)所有的代理类都扩展于 Proxy类。一个代理类只有一个实例域——调用处理器, 它定义在 Proxy的超类中, 为了履行代理对象的职责, 所需要的任何附加数据都必须存储在调用处理器中;
2.3)所有的代理类都覆盖了 Object类中的方法 toString、equals 和 hashCode 方法;这些方法仅仅 调用了 调用处理器 的invoke 方法;Object类中的其他方法没有被重新定义;
2.4)没有定义代理类的名字的话, Sun 虚拟机中的Proxy类将生成一个 以 字符串 $Proxy 开头的类名;
2.5)对于特定的类加载器和预设的一组接口来说, 只能有一个代理类;也就是说, 如果使用 同一个类加载器和接口数组调用两次 newProxyInstance 方法的话, 那么只能够得到同一个类的两个对象, 也可以利用 getProxyClass 方法获得这个类:

Class proxyClass = Proxy.getProxyClass(null,  interfaces);

2.6)代理类一定是 public 和 final, 如果代理类实现的所有接口都是 public, 代理类就不属于某个特定的包;否则,所有非公有的接口都必须属于同一个包;
2.7)可以调用 Proxy类中的 isProxyClass 方法 检测一个特定的 Class对象是否代表一个代理类;

[API] java.lang,reflect.InvocationHandler 1.3

Object invoke(Object proxy, Method method, Object[] args):返回实现指定接口的代理类;
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) : 构造一个实现指定接口的代理类的实例, 所有方法都将调用给定处理器对象的invoke 方法;
static boolean isProxyClass(Class c) : 如果 c 是一个代理类返回 true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值