今天一個偶然的機會我突然想看看JDK的動態代理,因為以前也知道一點,而且只是簡單的想測試一下使用,使用很快裡就寫好了這麼幾個接口和類:
接口類: UserService.java
package com.yixi.proxy; public interface UserService { public int save() ; public void update(int id); }
實作類別:UserServiceImpl.java
package com.yixi.proxy; public class UserServiceImpl implements UserService { @Override public int save() { System.out.println("user save...."); return 1; } @Override public void update(int id) { System.out.println("update a user " + id); } }
然後猴急猴急的就寫好了自己要的InvocationHandler:這個的功能是很簡單的就是記錄一下方法執行的開始時間和結束時間
TimeInvocationHandler .java
package com.yixi.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(proxy, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; } }
所有的準備工作都搞好了當然要開始寫測驗了!
Test.java
package com.yixi.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { 9 TimeInvocationHandler timeHandler = new TimeInvocationHandler(); UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); u.update(2); u.save(); } }
愉快地Run了一下,不過它並不給你面子結果是滿屏幕的異常:
startTime : 1352877835040 startTime : 1352877835040 startTime : 1352877835040 Exception in thread "main" java.lang.reflect.UndeclaredThrowableException at $Proxy0.update(Unknown Source) at com.yixi.proxy.Test.main(Test.java:11) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) ... 2 more
com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)異常明確告訴了是在TimeInvocationHandle的12行的問題:也就是
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(proxy, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; }從方法上來看沒什麼錯誤啊!因為在invoke()這個方法上貌似提供了method.invoke(Object,Object[])所要的所有的參數,我們會理所當然的去使用它,如果你真那樣想的話那你就中了JDK的陷阱了,先看下正確的寫法吧防止有些同學沒心情看後面的至少給個正確的解法:
修改TimeInvocationHandler.java
package com.yixi.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { private Object o; public TimeInvocationHandler(Object o){ this.o = o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(o, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; } }
修改Test.java
package com.yixi.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl()); UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); u.update(2); u.save(); } }
startTime : 1352879531334 update a user 2 endTime : 1352879531334 startTime : 1352879531334 user save.... endTime : 1352879531335如果想程式碼少一點的話可以直接寫匿名類別:
package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Pro.Proxy ;
public class Test {
public static void main(String[] args) {
final UserServiceImpl usi = new UserServiceImpl);🜠 usi.getClass().getClassLoader(),
usi.getClass().getInterfaces(),
@Override
public Object invoke(Object proxy, throws Throwable {
" +System.currentTimeMillis());
System.out.println("endTime : " +System.currentTimeMillis());
}
}) ;
u.update(2);
u.save();
}
}
參數既然method.invoke(Intargets)是那麼傳入的第一個目標Object proxy參數幹嘛? 還是往下看吧!
對於最重要的invoke這個方法(個人覺得)我們看下JDK是怎麼說的吧:
invoke Object invoke(Object proxy, Method method, Object[] args) throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 参数: proxy - 在其上调用方法的代理实例 method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。 args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
protected InvocationHandler h; protected Proxy(InvocationHandler h) { this.h = h; }
h.invoke(this,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();
当Test.java改成这样时:
public class Test { public static void main(String[] args) { final UserServiceImpl usi = new UserServiceImpl(); UserService u = (UserService) Proxy.newProxyInstance( usi.getClass().getClassLoader(), usi.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); u.update(2); u.save(); } }
注意这时候的匿名类的方法的返回的是null,运行一下就会发现:
Exception in thread "main" java.lang.NullPointerException at $Proxy0.save(Unknown Source) 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类中的两个方法:
public interface UserService { public int save() ; public void update(int id); }Save()方法返回的是int型的 而update方法返回的是void型的;根据上面的猜测是 handler.invoke()是实现 proxyClass.update(2);的,invoke方法中的return方法的是相应的代理方法的返回值,
InvocationHandler中invoke方法中第一个参数proxy貌似只是为了让Proxy类能给自己的InvocationHandler对象的引用调用方法时能传入代理对象proxyClass的引用,来完成proxyClass需要完成的业务。
更多代理模式之Java動態代理實作方法相关文章请关注PHP中文网!