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

java动态代理(转载)

2010-03-22 21:41 316 查看
动态代理是很多框架和技术的基础, spring 的AOP实现就是基于动态代理实现的。了解动态代理的机制对于理解AOP的底层实现是很有帮助的。

查看doc文档就可以知道,在java.lang.reflect包中有一个叫Proxy的类。下面是doc文档对Proxy类的说明:

"A dynamic proxy class
(simply referred to as a proxy class
below) is a class that implements a list of interfaces specified at
runtime when the class is created, with behavior as described below. A proxy interface
is such an interface that is implemented by a proxy class. A proxy instance
is an instance of a proxy class. Each proxy instance has an associated invocation handler
object, which implements the interface
InvocationHandler


."

Proxy类的设计用到代理模式的设计思想,Proxy类对象实现了代理目标的所有接口,并代替目标对象进行实际的操作。但这种替
代不是一种简单的替代,这样没有任何意义,代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截。所
以,Proxy应该包括一个方法拦截器,来指示当拦截到方法调用时作何种处理。InvocationHandler就是拦截器的接口。

InvocationHandler接口也是在java.lang.reflec

Object invoke


(
Object

proxy,
Method

method,
Object

[] args)

这个接口有三个参数,其中第二和第三个参数都比较好理解,一个是被拦截的方法,一个是该方法的参数列表。关键是第一个参数。按照doc文档的解析,

proxy
- the proxy instance that the method was invoked on

也就是说,proxy应该是一个代理实例,但为什么要传入这个参数呢?

带着这个问题,自己编了个小程序作了一点试验。

///////////////////////////////////////

public interface IAnimal {

void info();

}

////////////////////////////////////

public class Dog implements IAnimal

{

public void info() {

System.out.println("I am a dog!");

}

}

///////////////////////////////////////

import java.lang.reflect.*;

public class ProxyTest {

public static void main(String[] args) throws InterruptedException {

final IAnimal animal = new Dog();

Object proxyObj =Proxy.newProxyInstance(

animal.getClass().getClassLoader(),

animal.getClass().getInterfaces(),

new InvocationHandler()

{

public Object invoke(Object proxy, Method method, Object[] args)

{

try {

System.out.println("被拦截的方法:" + method.getName());

return method.invoke(animal, args);

}

catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return null;

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return null;

} catch (InvocationTargetException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return null;

}

}

});

if(proxyObj instanceof IAnimal)

{

System.out.println("the proxyObj is an animal!");

}

else

{

System.out.println("the proxyObj isn't an animal!");

}

if(proxyObj instanceof Dog)

{

System.out.println("the proxyObj is a dog!");

}

else

{

System.out.println("the proxyObj isn't a dog!");

}

IAnimal animalProxy = (IAnimal)proxyObj;

animalProxy.info();

animalProxy.hashCode();

System.out.println(animalProxy.getClass().getName().toString());

}

}

程序执行的结果如下:

the proxyObj is an animal!

the proxyObj isn't a dog!

被拦截的方法:info

I am a dog!

被拦截的方法:hashCode

$Proxy0

从结果可以看出以下几点:

1. proxyObj 是一个实现了目标对象接口的对象,而不同于目标对象。也就是说,这种代理机制是面向接口,而不是面向类的。

2. info方法(在接口中)被成功拦截了,hashCode方法也成功被拦截了,但意外的是,getClass方法(继承自Object 类的方法)并没有被拦截!!

3. 应用调试还可以看出Invocation接口中invoke方法的传入的proxy参数确实就是代理对象实例proxyObj

为何getClass()没有被拦截?proxy参数又有何用呢?

先不管,做一个试验看看。既然这个proxy参数就是代理实例对象,它理所当然和proxyObj是一样的,可以调用info等方法。于是我们可以在invoke方法中加上如下一条语句:

((IAnimal)proxy).info();

结果是:

the proxyObj is an animal!

the proxyObj isn't a dog!

被拦截的方法:info

被拦截的方法:info

.......

被拦截的方法:info

被拦截的方法:info

然后就是栈溢出

结果是很明显的,在invoke方法中调用proxy中的方法会再一次引发invoke方法,这就陷入了死循环,最终结果当然是栈溢出的。

可以在invoke方法中调用proxy.getClass(), 程序可以正常运行。但如果调用hashCode()方法同样会导致栈溢出。

通过上面的试验,可以得出一些初步结论,invoke
接口中的proxy参数不能用于调用所实现接口的方法。奇怪的是hashCode()和getClass()方法都是从Object中继承下来的方法,为
什么一个可以另一个不可以呢?带首疑问到doc文档看一下Object中这两个方法,发现getClass()是定义为final的,而
hashCode()不是。难道是这个原因,于是找到一个非final方法,如equals试了一下,真的又会导致栈溢出;找另一个final方法如
wait(),试了一下,invoke又不拦截了。final 难道就是关键之处?

还有一个问题就是proxy有什么用?既然proxy可以调用getClass()方法,我们就可以得到proxy的Class类象,
从而可以获得关于proxy代理实例的所有类信息,如方法列表,Annotation等,这就为我们提供的一个分析proxy的有力工具,如通过分析
Annotation分析方法的声明式事务需求。我想传入proxy参数应该是这样一个用意吧。

InvocationHandler的资料

属于包:java.lang.reflect

public interface InvocationHandler


InvocationHandler

是代理实例的调用处理程序
实现的接口。

每个代码实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的
invoke

方法。

从以下版本开始:

JDK1.3

invoke

Object
invoke
(Object
proxy,

Method
method,

Object
[] args)

throws Throwable

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数:

proxy

- 在其上调用方法的代理实例

method

- 对应于在代理实例上调用的接口方法的
Method

实例。
Method

对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args

- 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为
null

。基本类型的参数被包装在适当基本包装器类(如
java.lang.Integer

java.lang.Boolean

)的实例中。

返回:

从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为
null

并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出
NullPointerException

。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出
ClassCastException



抛出:

Throwable

- 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的
throws

子句中声明的任一异常类型或未经检查的异常类型
java.lang.RuntimeException

java.lang.Error

。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的
throws

子句中声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的
UndeclaredThrowableException


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: