学习Java的同学注意了!!!
学习进程中遇到甚么问题或想获得学习资源的话,欢迎加入Java学习交换群,群号码:183993990 我们1起学Java!
今天1个偶然的机会我突然想看看JDK的动态代理,由于之前也知道1点,而且只是简单的想测试1下使用,使用很快里就写好了这么几个接口和类:
接口类:UserService.java
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 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:这个的功能是很简单的就是记录1下方法履行的开始时间和结束时间
TimeInvocationHandler.java
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 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下,不过它其实不给你面子 结果是满屏幕的异常:
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 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 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 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点的话可以直接写匿名类:
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);中第1个参数是传入的是目标对象 那末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个构造方法:
1 protected InvocationHandler h; 2 3 protected Proxy(InvocationHandler h) { 4 this.h = h; 5 }
把InvocationHandler作为Proxy的构造方法的参数....那它要InvocationHandler干甚么用呢?跟InvocationHandler中的invoke()方法有甚么联系吗?
我第1个想到的是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();
所以1开始的毛病是对InvocationHandler的invoke方法的理解的毛病! 全部的invoke()方法
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 }
其实就是代理对象的1个代理方法,履行代理对象的1个方法就会访问1次invoke()方法;在invoke方法中的Object obj = method.invoke(usi, args); 是按原对象本应当履行的方式履行,该返回甚么就返回甚么。不知道你能想到点甚么啵。下面来验证1下:
当Test.java改成这样时:
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下就会发现:
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的原因:
注意1下UserService类中的两个方法:
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方法中第1个参数proxy貌似只是为了让Proxy类能给自己的InvocationHandler对象的援用调用方法时能传入代理对象proxyClass的援用,来完成proxyClass需要完成的业务。
学习Java的同学注意了!!!
学习进程中遇到甚么问题或想获得学习资源的话,欢迎加入Java学习交换群,群号码:183993990 我们1起学Java!