您的位置:首页 > 编程语言 > Java开发

JavaSE第六十六讲:Java中的动态代理模式详解

2013-01-05 18:01 555 查看
1. 结合上一讲所讲内容的代理模式程序,猜想如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法

public object invoke(Object obj,Method method, Object[] args)

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,作用类似于上一讲例中的ProxySubject,其中主要包含以下内容

1) protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

2) static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

3) static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口[在上一讲程序中的例子]中声明过的方法)

   所谓Dynamic Proxy(动态代理)是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在了解了动态代理类的基本过程之后,我们一起来看JDK Doc文档里面的这两个类的说明:

java.lang.reflect 

Interface InvocationHandler [注意这是一个接口]

public interface InvocationHandler

  InvocationHandler is the interface implemented by the invocation handler[调用处理器] of a proxy instance. 

  Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched[派发] to the invoke method of its
invocation handler. 

[InvocationHandler是一个接口,它是有代理实例的一个调用处理器实现的

 每一个代理实例都有一个与之关联的调用处理器,当我们调用代理实例的一个方法的时候,这个方法就会被编码并派发到它的与之关联的调用处理器的 invoke() 上]

继续跟踪这个接口的 invoke()方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with. 

[它会处理一个代理实例的方法调用,并返回这个结果(这里面的意思是当我们调用代理实例的某一个方法的时候,这个方法实际上并不是由代理去完成的,而是由与之相关联的InvocationHandler的invoke()方法帮助完成的),当代理实例的一个方法被调用的时候,这个方法会在与之相关联的调用处理器上被调用,如果65-1所示]



图65-1

查看invoke()方法的参数含义:这几个参数都很重要

Parameters:

proxy - the proxy instance that the method was invoked on [代理实例的方法被调用,也就是将调用 proxy 这个代理实例的某一个方法]method - the Method instance corresponding to the interface method invoked on the proxy instance. The declaring class of the Method object will be the interface that
the method was declared in, which may be a superinterface of the proxy interface that the proxy class inherits the method through.[这个Method实例对应于代理实例的那个接口方法,换句话说加入我调用代理实例的一个output()方法,那么这里的method就是所对应与output的Method对象。]

args - an array of objects containing the values of the arguments passed in the method invocation on the proxy instance, or null if interface method takes no arguments. Arguments
of primitive types are wrapped in instances of the appropriate primitive wrapper class, such as java.lang.Integer or java.lang.Boolean.[这边的args表示当你要调用一个方法的时候,这个方法需要接受参数,这些参数都会以数组的形式传递给invoke()这个方法,如果没有参数的话,这数组长度为空]

现在查看Proxy这个类的JDK Doc说明:

java.lang.reflect 

Class Proxy

java.lang.Object

  java.lang.reflect.Proxy

public class Proxyextends Object implements Serializable

  Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

[Proxy提供了静态方法用来创建动态代理类和实例,它也是由那些方法所创建的动态代理类的父类,换句话说当使用Java的动态代理时,我们所创造的动态代理类都是它的子类]

查看这个类的一些方法,如下图所示这个方法:



这个方法接受一个InvocationHandle的参数,这个就是用来实现代理实例和调用处理器的关联使用的。

查看里面的方法,其中最重要的一个方法是newProxyInstance()方法,如下所示

newProxyInstance

public static Object newProxyInstance(ClassLoader loader,

                                      Class<?>[] interfaces,

                                      InvocationHandler h)

                               throws IllegalArgumentException

[创建一个新的代理实例,查看里面的参数:

loader---类转载器,创建一个具体实例的时候,是由那一个类转载器去转载的,我们在Java中所创建的对象,类都是由类加载器加载进来的。

interfaces ---表示这个代理要实现哪一个接口,接口所构成的数组

h --- 传递一个InvocationHandler类型参数,它会传递给Proxy构造方法里面的参数。]

2. 动态代理涉及到的方法,其中第三个尤为重要

1) protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

2) static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

3) static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)[也就是可以当作真实对象来使用,当我们使用这个方法时候,返回的是一个代理对象,这个代理对象可以当作真实对象来使用,完成真实对象所做的操作,还可以附加其他的操作]

3. 所谓Dynamic Proxy是这样一种class:它是在运行时生成的class(以前的类都是定义好的:public class...),在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic
Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作.在使用动态代理类时,我们必须实现InvocationHandler接口.

写一个程序例子实现Java的动态代理

package com.ahuier.dynamicproxy;

/*
* 抽象角色
* 上一个例子我们可以用接口也可以用抽象类
* 但是在动态代理中我们必须用接口来实现,因为在方法 newProxyInstance()参数传递的接口的数组
*/
public interface Subject {
public void request();
}
package com.ahuier.dynamicproxy;

/*
* 真实角色
*/
public class RealSubject implements Subject {

@Override
public void request() {
System.out.println("From the RealSupject.");
}

}
package com.ahuier.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/*
* 动态代理角色
* 使用动态代理必须先实现 InvocationHandler 这个接口
*/
public class DynamicSubject implements InvocationHandler {

//代理角色存在一个对真实对象的引用,动态代理可以代理不同的真实对象,所以这边用Object类型以接受所有对象
private Object sub;
public DynamicSubject(Object obj){
this.sub = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

System.out.println("before calling" + method);

//使用反射调,参数sub表示用构造方法传过来的 Object sub 这个对象.
method.invoke(sub, args);
System.out.println(args == null);
System.out.println("after calling" + method);

return null;
}

}
package com.ahuier.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/*
* 客户端
*/
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); // 代理谁就生成谁

InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();

// 下面的代码一次性生成代理
Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
subject.request();
System.out.println(subject.getClass());
}

}
编译执行结果:
before callingpublic abstract void com.ahuier.dynamicproxy.Subject.request()

From the RealSupject.

true

after callingpublic abstract void com.ahuier.dynamicproxy.Subject.request()

class $Proxy0

【说明】:DynamicSubject代理类,该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象,此外,改类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以再执行真实对象的方法前后加入自己的一些额外方法。

$Proxy0 这个类是在运行期间动态所生成的一个类。所以其实例是它的一个实例。

注意学好动态代理之后,在之后学习Sprint框架是非常重要的,因为这些框架都是基于动态代理的,学好动态代理相当于学习好一般框架了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: