JDK中的动态代理
来源:互联网 发布:配眼镜 知乎 编辑:程序博客网 时间:2024/05/22 00:27
ava 动态代理
今天一个偶然的机会我突然想看看JDK的动态代理,因为以前也知道一点,而且只是简单的想测试一下使用,使用很快里就写好了这么几个接口和类:
接口类:UserService.java
复制代码
复制代码
实现类:UserServiceImpl.java
复制代码
复制代码
然后猴急猴急的就写好了自己要的InvocationHandler:这个的功能是很简单的就是记录一下方法执行的开始时间和结束时间
TimeInvocationHandler.java
复制代码
复制代码
所有的准备工作都弄好了 当然要开始写测试了!
Test.java
复制代码
复制代码
愉快地Run了一下,不过它并不给你面子 结果是满屏幕的异常:
复制代码
复制代码
com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
异常明确告诉了是在TimeInvocationHandle的12行的问题:也就是
复制代码
复制代码
从方法上来看没什么错误啊!因为在invoke()这个方法上貌似提供了method.invoke(Object,Object[])所要的所有的参数,我们会理所应当的去使用它,如果你真那样想的话 那你就中了JDK的陷阱了,先看下正确的写法吧 防止有些同学没心情看后面的 至少给个正确的解法:
修改TimeInvocationHandler.java
复制代码
复制代码
修改Test.java
复制代码
复制代码
现在是正确的输出结果:
复制代码
1 startTime : 1352879531334
2 update a user 2
3 endTime : 1352879531334
4 startTime : 1352879531334
5 user save....
6 endTime : 1352879531335
复制代码
如果想代码少一点的话可以直接写匿名类:
复制代码
复制代码
既然method.invoke(target,args);中第一个参数是传入的是目标对象 那么invocationHandler的Invoke方法要个Object proxy参数干嘛呢 ? 还是往下看吧!
对于最重要的invoke这个方法(个人觉得)我们看下JDK是怎么说的吧:
复制代码
1 invoke
2 Object invoke(Object proxy,
3 Method method,
4 Object[] args)
5 throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
6
7 参数:
8 proxy - 在其上调用方法的代理实例
9 method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
10 args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
复制代码
proxy - 在其上调用方法的代理实例 ? 这句话是什么意思呢? 代理? method是代理的方法? 那我执行代理的method不是就应该是Object obj = method.invoke(proxy, args);吗? 当时我也没转过弯来,去讨论群,去google都没找到什么灵感,想想还是这个看看源码吧 也许能看到点什么!
打开Proxy类的源码发现有这么一个构造方法:
1 protected InvocationHandler h;
2
3 protected Proxy(InvocationHandler h) {
4 this.h = h;
5 }
把InvocationHandler作为Proxy的构造方法的参数....那它要InvocationHandler干什么用呢?跟InvocationHandler中的invoke()方法有什么联系吗?
我第一个想到的是Proxy内部会调用下面的语句:
1 h.invoke(this,this.getClass().getMethod(methodName),args);
因为总得去调用invoke方法才能执行相应的method方法吧,
我们先来看下这个
在这里你就会发现貌似有点感觉了:当u.update(2)时 Proxy就会调用 handler.invoke(proxyClass,update,2) 也就是调用了proxyClass.update(2);
当u.save();时 Proxy就会调用handler.invoke(proxyClass,save,null) 也就是调用了proxyClass.save();
所以一开始的错误是对InvocationHandler的invoke方法的理解的错误! 整个的invoke()方法
复制代码
复制代码
其实就是代理对象的一个代理方法,执行代理对象的一个方法就会访问一次invoke()方法;在invoke方法中的Object obj = method.invoke(usi, args); 是按原对象本应该执行的方式执行,该返回什么就返回什么。不知道你能想到点什么啵。下面来验证一下:
当Test.java改成这样时:
复制代码
复制代码
注意这时候的匿名类的方法的返回的是null,运行一下就会发现:
1 Exception in thread "main" java.lang.NullPointerException
2 at $Proxy0.save(Unknown Source)
3 at com.yixi.proxy.Test.main(Test.java:17)
17行有空指针 也就是这里的u.save()方法有为null的元素 难道是u是空的? 不应该啊如果u是null的话那么u.update(2)在那里就会报空指针异常了,当我把17行注释掉以后异常没了说明u.update()能正常执行。那这到底是为什么呢?
其实这就是invoke方法返回null的缘故:
注意一下UserService类中的两个方法:
复制代码
复制代码
Save()方法返回的是int型的 而update方法返回的是void型的;根据上面的猜测是 handler.invoke()是实现 proxyClass.update(2);的,invoke方法中的return方法的是相应的代理方法的返回值,
所以在invoke方法返回null的时候代理的update方法接收到返回值是null, 而它本来就是返回void 所以没有报异常, 而代理save必须返回int型的数值 我们这返回的还是null,JVM无法将null转化为int型 所以就报了异常了
这样解释就能解释通了,也能相对证明前面的猜测。
今天一个偶然的机会我突然想看看JDK的动态代理,因为以前也知道一点,而且只是简单的想测试一下使用,使用很快里就写好了这么几个接口和类:
接口类:UserService.java
复制代码
1
2
3
4
5
6
7
8
9
1
package
com.yixi.proxy;
2
3
public
interface
UserService {
4
5
public
int
save() ;
6
7
public
void
update(
int
id);
8
9
}
实现类:UserServiceImpl.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
package
com.yixi.proxy;
2
3
public
class
UserServiceImpl
implements
UserService {
4
5
@Override
6
public
int
save() {
7
System.out.println(
"user save...."
);
8
return
1
;
9
}
10
11
@Override
12
public
void
update(
int
id) {
13
System.out.println(
"update a user "
+ id);
14
}
15
16
}
复制代码
然后猴急猴急的就写好了自己要的InvocationHandler:这个的功能是很简单的就是记录一下方法执行的开始时间和结束时间
TimeInvocationHandler.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
package
com.yixi.proxy;
2
3
import
java.lang.reflect.InvocationHandler;
4
import
java.lang.reflect.Method;
5
6
public
class
TimeInvocationHandler
implements
InvocationHandler {
7
8
@Override
9
public
Object invoke(Object proxy, Method method, Object[] args)
10
throws
Throwable {
11
System.out.println(
"startTime : "
+System.currentTimeMillis());
12
Object obj = method.invoke(proxy, args);
13
System.out.println(
"endTime : "
+System.currentTimeMillis());
14
return
obj;
15
}
16
17
}
复制代码
所有的准备工作都弄好了 当然要开始写测试了!
Test.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
1
package
com.yixi.proxy;
2
import
java.lang.reflect.Proxy;
3
4
public
class
Test {
5
6
public
static
void
main(String[] args) {
7
TimeInvocationHandler timeHandler =
new
TimeInvocationHandler();
8
UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.
class
.getClassLoader(), UserServiceImpl.
class
.getInterfaces(), timeHandler);
9
u.update(
2
);
10
u.save();
11
}
12
}
愉快地Run了一下,不过它并不给你面子 结果是满屏幕的异常:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
1
startTime :
1352877835040
2
startTime :
1352877835040
3
startTime :
1352877835040
4
Exception in thread
"main"
java.lang.reflect.UndeclaredThrowableException
5
at $Proxy0.update(Unknown Source)
6
at com.yixi.proxy.Test.main(Test.java:
11
)
7
Caused by: java.lang.reflect.InvocationTargetException
8
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
9
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39
)
10
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25
)
11
at java.lang.reflect.Method.invoke(Method.java:
597
)
12
at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:
12
)
13
...
2
more
复制代码
com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
异常明确告诉了是在TimeInvocationHandle的12行的问题:也就是
复制代码
1
2
3
4
5
6
7
1
public
Object invoke(Object proxy, Method method, Object[] args)
2
throws
Throwable {
3
System.out.println(
"startTime : "
+System.currentTimeMillis());
4
Object obj = method.invoke(proxy, args);
5
System.out.println(
"endTime : "
+System.currentTimeMillis());
6
return
obj;
7
}
复制代码
从方法上来看没什么错误啊!因为在invoke()这个方法上貌似提供了method.invoke(Object,Object[])所要的所有的参数,我们会理所应当的去使用它,如果你真那样想的话 那你就中了JDK的陷阱了,先看下正确的写法吧 防止有些同学没心情看后面的 至少给个正确的解法:
修改TimeInvocationHandler.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
package
com.yixi.proxy;
2
3
import
java.lang.reflect.InvocationHandler;
4
import
java.lang.reflect.Method;
5
6
public
class
TimeInvocationHandler
implements
InvocationHandler {
7
8
private
Object o;
9
10
public
TimeInvocationHandler(Object o){
11
this
.o = o;
12
}
13
14
@Override
15
public
Object invoke(Object proxy, Method method, Object[] args)
16
throws
Throwable {
17
System.out.println(
"startTime : "
+System.currentTimeMillis());
18
Object obj = method.invoke(o, args);
19
System.out.println(
"endTime : "
+System.currentTimeMillis());
20
return
obj;
21
}
22
23
}
复制代码
修改Test.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
1
package
com.yixi.proxy;
2
3
import
java.lang.reflect.Proxy;
4
5
public
class
Test {
6
7
public
static
void
main(String[] args) {
8
TimeInvocationHandler timeHandler =
new
TimeInvocationHandler(
new
UserServiceImpl());
9
UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.
class
.getClassLoader(), UserServiceImpl.
class
.getInterfaces(), timeHandler);
10
u.update(
2
);
11
u.save();
12
}
13
}
复制代码
现在是正确的输出结果:
复制代码
1 startTime : 1352879531334
2 update a user 2
3 endTime : 1352879531334
4 startTime : 1352879531334
5 user save....
6 endTime : 1352879531335
复制代码
如果想代码少一点的话可以直接写匿名类:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
package
com.yixi.proxy;
2
3
import
java.lang.reflect.InvocationHandler;
4
import
java.lang.reflect.Method;
5
import
java.lang.reflect.Proxy;
6
7
public
class
Test {
8
9
public
static
void
main(String[] args) {
10
final
UserServiceImpl usi =
new
UserServiceImpl();
11
UserService u = (UserService) Proxy.newProxyInstance(
12
usi.getClass().getClassLoader(),
13
usi.getClass().getInterfaces(),
14
new
InvocationHandler() {
15
16
@Override
17
public
Object invoke(Object proxy, Method method, Object[] args)
18
throws
Throwable {
19
System.out.println(
"startTime : "
+System.currentTimeMillis());
20
Object obj = method.invoke(usi, args);
21
System.out.println(
"endTime : "
+System.currentTimeMillis());
22
return
obj;
23
}
24
});
25
u.update(
2
);
26
u.save();
27
}
28
}
复制代码
既然method.invoke(target,args);中第一个参数是传入的是目标对象 那么invocationHandler的Invoke方法要个Object proxy参数干嘛呢 ? 还是往下看吧!
对于最重要的invoke这个方法(个人觉得)我们看下JDK是怎么说的吧:
复制代码
1 invoke
2 Object invoke(Object proxy,
3 Method method,
4 Object[] args)
5 throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
6
7 参数:
8 proxy - 在其上调用方法的代理实例
9 method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
10 args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
复制代码
proxy - 在其上调用方法的代理实例 ? 这句话是什么意思呢? 代理? method是代理的方法? 那我执行代理的method不是就应该是Object obj = method.invoke(proxy, args);吗? 当时我也没转过弯来,去讨论群,去google都没找到什么灵感,想想还是这个看看源码吧 也许能看到点什么!
打开Proxy类的源码发现有这么一个构造方法:
1 protected InvocationHandler h;
2
3 protected Proxy(InvocationHandler h) {
4 this.h = h;
5 }
把InvocationHandler作为Proxy的构造方法的参数....那它要InvocationHandler干什么用呢?跟InvocationHandler中的invoke()方法有什么联系吗?
我第一个想到的是Proxy内部会调用下面的语句:
1 h.invoke(this,this.getClass().getMethod(methodName),args);
因为总得去调用invoke方法才能执行相应的method方法吧,
我们先来看下这个
在这里你就会发现貌似有点感觉了:当u.update(2)时 Proxy就会调用 handler.invoke(proxyClass,update,2) 也就是调用了proxyClass.update(2);
当u.save();时 Proxy就会调用handler.invoke(proxyClass,save,null) 也就是调用了proxyClass.save();
所以一开始的错误是对InvocationHandler的invoke方法的理解的错误! 整个的invoke()方法
复制代码
1
2
3
4
5
6
7
8
1
@Override
2
public
Object invoke(Object proxy, Method method, Object[] args)
3
throws
Throwable {
4
System.out.println(
"startTime : "
+System.currentTimeMillis());
5
Object obj = method.invoke(usi, args);
6
System.out.println(
"endTime : "
+System.currentTimeMillis());
7
return
obj;
8
}
其实就是代理对象的一个代理方法,执行代理对象的一个方法就会访问一次invoke()方法;在invoke方法中的Object obj = method.invoke(usi, args); 是按原对象本应该执行的方式执行,该返回什么就返回什么。不知道你能想到点什么啵。下面来验证一下:
当Test.java改成这样时:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
public
class
Test {
2
3
public
static
void
main(String[] args) {
4
final
UserServiceImpl usi =
new
UserServiceImpl();
5
UserService u = (UserService) Proxy.newProxyInstance(
6
usi.getClass().getClassLoader(),
7
usi.getClass().getInterfaces(),
8
new
InvocationHandler() {
9
10
@Override
11
public
Object invoke(Object proxy, Method method, Object[] args)
12
throws
Throwable {
13
return
null
;
14
}
15
});
16
u.update(
2
);
17
u.save();
18
}
19
}
复制代码
注意这时候的匿名类的方法的返回的是null,运行一下就会发现:
1 Exception in thread "main" java.lang.NullPointerException
2 at $Proxy0.save(Unknown Source)
3 at com.yixi.proxy.Test.main(Test.java:17)
17行有空指针 也就是这里的u.save()方法有为null的元素 难道是u是空的? 不应该啊如果u是null的话那么u.update(2)在那里就会报空指针异常了,当我把17行注释掉以后异常没了说明u.update()能正常执行。那这到底是为什么呢?
其实这就是invoke方法返回null的缘故:
注意一下UserService类中的两个方法:
复制代码
1
2
3
4
5
6
7
1
public
interface
UserService {
2
3
public
int
save() ;
4
5
public
void
update(
int
id);
6
7
}
复制代码
Save()方法返回的是int型的 而update方法返回的是void型的;根据上面的猜测是 handler.invoke()是实现 proxyClass.update(2);的,invoke方法中的return方法的是相应的代理方法的返回值,
所以在invoke方法返回null的时候代理的update方法接收到返回值是null, 而它本来就是返回void 所以没有报异常, 而代理save必须返回int型的数值 我们这返回的还是null,JVM无法将null转化为int型 所以就报了异常了
这样解释就能解释通了,也能相对证明前面的猜测。
InvocationHandler中invoke方法中第一个参数proxy貌似只是为了让Proxy类能给自己的InvocationHandler对象的引用调用方法时能传入代理对象proxyClass的引用,来完成proxyClass需要完成的业务。
出处:http://bbs.csdn.net/topics/390527741
0 0
- JDK中的动态代理
- JDK中的动态代理
- JDK中的动态代理
- JDK中的动态代理
- jdk中的动态代理
- JDK中的动态代理
- JDK中的动态代理
- JDK中的动态代理
- Java中的动态代理--JDK动态代理
- JDK中的动态代理之一
- JDK中的动态代理之一
- JDK中的AOP、动态代理
- JDK中的动态代理机制
- JDK中的proxy动态代理
- JDK中的proxy动态代理
- spring中的jdk动态代理
- Java JDK中的静态代理、动态代理&Cglib动态代理
- Java JDK中的静态代理、动态代理&Cglib动态代理
- select函数
- android 切换至surfaceView闪屏(黑屏)
- hdoj maxsum 1003
- java算法之字符串全排列 。如此精辟
- FreeRTOS 特性简介
- JDK中的动态代理
- MyEclipse常用快捷键
- Android Studio系列教程一--下载与安装
- 统计数组中元素出现的次数——Java小程序
- C++宽字符串转字符串
- 实现的效果:EditText提示文本字体大小和输入文本字体大小不同
- Java中的Hashtable实现方法
- Android UI 之 SeekBar
- Android 实现能够暂停的录音功能