JDK的动态代理机制
来源:互联网 发布:壳域名仅限局域网使用 编辑:程序博客网 时间:2024/06/05 10:35
jdk的动态代理是基于接口的,必须实现了某一个或多个任意接口才可以被代理,并且只有这些接口中的方法会被代理。看了一下jdk带的动态代理api,发现没有例子实在是很容易走弯路,所以这里写一个加法器的简单示例。
1
// Adder.java
2
3
package
test;
4
5
public
interface
Adder {
6
int
add(
int
a,
int
b);
7
}
01
// AdderImpl.java
02
03
package
test;
04
05
public
class
AdderImpl
implements
Adder {
06
@Override
07
public
int
add(
int
a,
int
b) {
08
return
a + b;
09
}
10
}
1
现在我们有一个接口Adder以及一个实现了这个接口的类AdderImpl,写一个Test测试一下。
01
// Test.java
02
03
package
test;
04
05
public
class
Test {
06
public
static
void
main(String[] args)
throws
Exception {
07
Adder calc =
new
AdderImpl();
08
int
result = calc.add(
1
,
2
);
09
System.out.println(
"The result is "
+ result);
10
}
11
}
很显然,控制台会输出:
1
The result is 3
然而现在我们需要在加法器使用之后记录一些信息以便测试,但AdderImpl的源代码不能更改,就像这样:
1
Proxy: invoke add() at 2009-12-16 17:18:06
2
The result is 3
动态代理可以很轻易地解决这个问题。我们只需要写一个自定义的调用处理器(实现接口java.lang.reflect.InvokationHandler),然后使用类java.lang.reflect.Proxy中的静态方法生成Adder的代理类,并把这个代理类当做原先的Adder使用就可以。
第一步:实现InvokationHandler,定义调用方法时应该执行的动作。
自定义一个类MyHandler实现接口java.lang.reflect.InvokationHandler,需要重写的方法只有一个:
01
// AdderHandler.java
02
03
package
test;
04
05
import
java.lang.reflect.InvocationHandler;
06
import
java.lang.reflect.Method;
07
08
class
AdderHandler
implements
InvocationHandler {
09
/**
10
* @param proxy 接下来Proxy要为你生成的代理类的实例,注意,并不是我们new出来的AdderImpl
11
* @param method 调用的方法的Method实例。如果调用了add(),那么就是add()的Method实例
12
* @param args 调用方法时传入的参数。如果调用了add(),那么就是传入add()的参数
13
* @return 使用代理后将作为调用方法后的返回值。如果调用了add(),那么就是调用add()后的返回值
14
*/
15
@Override
16
public
Object invoke(Object proxy, Method method, Object[] args)
17
throws
Throwable {
18
// ...
19
}
20
}
使用代理后,这个方法将取代指定的所有接口中的所有方法的执行。在本例中,调用adder.add()方法时,实际执行的将是invoke()。所以为了有正确的结果,我们需要在invoke()方法中手动调用add()方法。再看看invoke()方法的参数,正好符合反射需要的所有条件,所以这时我们马上会想到这样做:
1
Object returnValue = method.invoke(proxy, args);
如果你真的这么做了,那么恭喜你,你掉入了jdk为你精心准备的圈套。proxy是jdk为你生成的代理类的实例,实际上就是使用代理之后adder引用所指向的对象。由于我们调用了adder.add(1,2),才使得invoke()执行,如果在invoke()中使用method.invoke(proxy,args),那么又会使invoke()执行。没错,这是个死循环。然而,invoke()方法没有别的参数让我们使用了。最简单的解决方法就是,为MyHandler加入一个属性指向实际被代理的对象。所以,因为jdk的冷幽默,我们需要在自定义的Handler中加入以下这么一段:
1
// 被代理的对象
2
private
Object target;
3
4
public
AdderHandler(Object target) {
5
this
.target = target;
6
}
喜欢的话还可以加上getter/setter。接着,invoke()就可以这么用了:
01
// AdderHandler.java
02
03
package
test;
04
05
import
java.lang.reflect.InvocationHandler;
06
import
java.lang.reflect.Method;
07
import
java.util.Date;
08
09
class
AdderHandler
implements
InvocationHandler {
10
// 被代理的对象
11
private
Object target;
12
13
public
AdderHandler(Object target) {
14
this
.target = target;
15
}
16
17
@Override
18
public
Object invoke(Object proxy, Method method, Object[] args)
19
throws
Throwable {
20
// 调用被代理对象的方法并得到返回值
21
Object returnValue = method.invoke(target, args);
22
// 调用方法前后都可以加入一些其他的逻辑
23
System.out.println(
"Proxy: invoke "
+ method.getName() +
"() at "
+
new
Date().toLocaleString());
24
// 可以返回任何想要返回的值
25
return
returnValue;
26
}
27
}
第二步:使用jdk提供的java.lang.reflect.Proxy生成代理对象。
使用newProxyInstance()方法就可以生成一个代理对象。把这个方法的签名拿出来:
01
/**
02
* @param loader 类加载器,用于加载生成的代理类。
03
* @param interfaces 需要代理的接口。这些接口的所有方法都会被代理。
04
* @param h 第一步中我们建立的Handler类的实例。
05
* @return 代理对象,实现了所有要代理的接口。
06
*/
07
public
static
Object newProxyInstance(ClassLoader loader,
08
Class<?>[] interfaces,
09
InvocationHandler h)
10
throws
IllegalArgumentException
这个方法会做这样一件事情,他将把你要代理的全部接口用一个由代码动态生成的类类实现,所有的接口中的方法都重写为调用InvocationHandler.invoke()方法。这个类的代码类似于这样:
01
// 模拟Proxy生成的代理类,这个类是动态生成的,并没有对应的.java文件。
02
03
class
AdderProxy
extends
Proxy
implements
Adder {
04
protected
AdderProxy(InvocationHandler h) {
05
super
(h);
06
}
07
08
@Override
09
public
int
add(
int
a,
int
b) {
10
try
{
11
Method m = Adder.
class
.getMethod(
"add"
,
new
Class[] {
int
.
class
,
int
.
class
});
12
Object[] args = {a, b};
13
return
(Integer) h.invoke(
this
, m, args);
14
}
catch
(Throwable e) {
15
throw
new
RuntimeException(e);
16
}
17
}
18
}
据api说,所有生成的代理类都是Proxy的子类。当然,生成的这个类的代码你是看不到的,而且Proxy里面也是调用sun.XXX包的api生成;一般情况下应该是直接生成了字节码。然后,使用你提供的ClassLoader将这个类加载并实例化一个对象作为代理返回。
看明白这个方法后,我们来改造一下main()方法。
01
// Test.java
02
03
package
test;
04
05
import
java.lang.reflect.InvocationHandler;
06
import
java.lang.reflect.Proxy;
07
08
public
class
Test {
09
public
static
void
main(String[] args)
throws
Exception {
10
Adder calc =
new
AdderImpl();
11
12
// 类加载器
13
ClassLoader loader = Test.
class
.getClassLoader();
14
// 需要代理的接口
15
Class[] interfaces = {Adder.
class
};
16
// 方法调用处理器,保存实际的AdderImpl的引用
17
InvocationHandler h =
new
AdderHandler(calc);
18
// 为calc加上代理
19
calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h);
20
21
/* 什么?你说还有别的需求? */
22
// 另一个处理器,保存前处理器的引用
23
// InvocationHandler h2 = new XXOOHandler(h);
24
// 再加代理
25
// calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h2);
26
27
int
result = calc.add(
1
,
2
);
28
System.out.println(
"The result is "
+ result);
29
}
30
}
输出结果会是什么呢?
1
Proxy: invoke add() at 2009-12-16 18:21:33
2
The result is 3
对比一下之前的结果,你会发现这点东西写了我一个多小时。再来看看JDK有多懒:
target完全可以在代理类中生成。
实际方法都需要手动调用,可见代理类中重写所有的方法都只有一句话:return xxx.invoke(ooo);
不过这么写也有他的理由,target自己管理,方法你爱调不调 ﹃_﹃;如果他调了,InvocationHandler接口中恐怕就需要两个方法了,还要判断返回、处理参数等等。
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- JDK动态代理机制
- JDK动态代理机制
- JDK动态代理机制
- [java] JDK 的动态代理机制
- JDK动态代理机制分析
- JDK中的动态代理机制
- jdk动态代理机制1
- 动态代理[JDK]机制解析
- JDK的动态代理
- JDK的动态代理
- STL中的vector和deque对比
- [翻译]High Performance JavaScript(026)
- ASP一个网页处理所有问题(添加,修改,删除,列表)框架
- 求非完全二叉树第m层第k个节点
- 国产开源项目的七宗罪
- JDK的动态代理机制
- AudioRecord和AudioTrack类的使用
- VC++6.0编译环境介绍
- Oracle的使用
- [翻译]High Performance JavaScript(027)
- asp防SQL注入函数
- 在Windows下搭建Android2.2开发环境
- 服务器压力测试中遇到的问题.
- symbian相关基础知识