前言
java的动态代理机制的出现,使得java程序员不需要自己手工的实现代理类。只要使用jdk的代理类,并向代理类指定一组接口及要new出来真正要执行的对象。代理类则将所有的对方法的调用分派委托到各个具体的对象上去反射执行,在分派委托的过程中,开发人员还可以根据自己的情况调整委托的对象以及功能,非常的灵活。
代理:设计模式
代理模式是常用的一种设计模式,就是提供代理以控制对某个对象的控制。代理类负责对委托类预处理消息、并转发消息,以及进行消息被委托执行后的后续处理。
一般为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以对访问者来讲两者没什么区别。通过对代理类的访问能够有效的控制对委托类的访问,可以很好的隐藏和保护委托类对象,同时为实施不同的控制策略预留了很好的灵活性。java动态代理机制以近乎完美的实践了代理模式。
相关的类和接口
java动态代理相关的一些类和接口:
java.lang.reflect,Proxy:这是java动态代理机制的主类,提供了一组静态方法来为一组接口动态的的生成代理类及其对象。
//方法1:该方法用于获取指定对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy);
//方法2:该方法用于获取关联于指定类装载器和一组接口和一组接口的动态代理类的对象
static Class getProxyClass(ClassLoader loader , Class[] interfaces);
//方法3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl);
//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态处理类实例
static Object newProxyInstance(ClassLoader loader ,Class[] interface ,InvocationHandler h)
java .lang.reflect.invocationHandler:这是调用处理器接口,它自定义了一个invocation方法,用于集中处理在动态代理类对象上的方法调用,通常该方法中实现对委托类的代理访问。
//该方法负责集中处理动态代理类上的所有方法的调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个参数是调用方法的参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy , Method method ,Object[] args);
每次生成动态代理类时对象时都需要指定一个实现了该接口的调用处理对象(参见Proxy静态方法的方法4的第一个参数)
java.lang.ClassLoader:这是类的加载器,负载将类的字节码装载到java虚拟机上(jvm),并为其定义对象,然后该类才能使用。Proxy静态方法生成的动态代理类同样需要通过类加载器来进行加载才能使用,他与普通类的唯一区别就是他的字节码是在jvm在运行时动态生成的,而非预存在任何一个.class文件中。
每次生成动态代理类对象时都需要指定一个类装载器对象(参见Proxy静态方法4的第一个参数);
代理机制及其特点:
首先狼我们来了解下如何使用java动态代理:具体步骤如下:
1.通过实现InvocationHandler接口创建自己的调用处理器;
//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的
分派转发,其内部通常包含指向引用委托类实例的应用,用于真正执行转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(...);
//通过Proxy为包括Interface接口在内的一组接口动态的创建代理类的对象
Class clazz =Proxy.getProxyClass(classLoader , new Class[]{Inteface.class});
//通过反射从生成的类对象获取构造函数对象
Constructor constructor = clazz.getConstructor(new Class[]{InvocetionHandler.class});
//通过构造器函数对象创建动态代理类对象
Interface proxy =(Interface)constructor.newInstance(new Object[]{handler});
实际的使用过程要简单很多,因为Proxy的静态方法newProxyInstance已经为我们封装了步骤2到4的过程,所以简化后的
//InvocationHandlerImpl 实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派
InvocationHandler handler = new InvocationHandlerImpl(...);
动态代理类的一些特点:
1、包:如果所有的接口都是public的,那么它将被定义在顶层包(即包的路径为空),如果所有代理的接口中有非public的接口(接口不能被定义为Protect 或private的,所以其他的就是包访问级别的),那么它将被定义在接口所在的包,这样做的原因是为了最大程度的保证代理类的不会因为包的管理而无法被成功定义并访问;
2、类修饰符:该类具有final和public修饰符;意味着它可以被所有的类访问,但是不能被继承
3、类名:格式是$ProxyN,其中N是一个逐一递增的阿拉伯数字,表示Proxy类第N次生成的动态代理类,值得注意的是并不是每次都调用Proxy方法都使得N+1
4、类继承关系:该类继承了Proxy方法以及实现了所有代理的接口,这也是为什么代理类能够被安全的转换为所代理的类的接口的原因。
下面是一些代理类实例的特点:
每个实例都会关联一个调用处理器对象,可以通过Proxy提供静态的方法getInvocationHandler去获得代理类实例的调用处理器对象。在代理类实例上调用其代理接口的方法时,这些方法最终也会被分配给调用处理器的Invoke方法执行。。。。
被代理接口的一组接口的特点:
首先,要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。其次,这些接口对于类加载器必须是可见的。否则,类加载器将无法链接,将会导致类定义的失败。再次,需要被代理的所有public接口必须在同一个包中,,否则代理类也会失败。最后,接口的数目不能超过65535,这是jvm设定的限制。
下面是处理异常的特点:
从调用处理器接口声明的方法中可见理论上它能够抛出任何类型的异常,因为所有的异常皆实现了Throwable接口。事实上是否定的。原因是:子类实现父类的接口或覆盖父类的方法时,抛出的异常的范围必须在父类的范围内。
动态代理中实现生产代理类的代码的方法是:ProxyGenerator
java的动态代理美中不足的地方是:仅仅支持Interface代理的桎梏。这种设计注定了这个遗憾。