Heute wollte ich zufällig einen Blick auf den dynamischen Proxy von JDK werfen, weil ich vorher ein wenig darüber wusste und einfach seine Verwendung testen wollte. Ich habe in kurzer Zeit mehrere Schnittstellen und Klassen geschrieben :
Schnittstellenklasse: UserService.java
package com.yixi.proxy; public interface UserService { public int save() ; public void update(int id); }
Implementierungsklasse: 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); } }
Dann habe ich schnell den InvocationHandler geschrieben, den ich wollte: Die Funktion davon ist Es ist Es ist ganz einfach, die Start- und Endzeit der Methodenausführung aufzuzeichnen
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; } }
Jetzt, da alle Vorbereitungen abgeschlossen sind, ist es natürlich an der Zeit, mit dem Schreiben von Tests zu beginnen!
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(); } }
Laufen Sie glücklich, aber es gibt Ihnen kein Gesicht. Das Ergebnis ist eine Ausnahme, die den Bildschirm ausfüllt:
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 . Die Ausnahme TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) zeigt deutlich, dass das Problem in Zeile 12 von TimeInvocationHandle liegt: Das heißt,
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; }An der Methode ist nichts falsch! Da die invoke()-Methode scheinbar alle von method.invoke(Object, Object[]) benötigten Parameter bereitstellt, werden wir sie selbstverständlich verwenden. Wenn Sie wirklich so denken, dann haben Sie das JDK getäuscht Eine Falle, schauen wir uns zuerst die richtige Schreibweise an. Falls einige Schüler keine Lust haben, Folgendes zu lesen, geben Sie mir zumindest die richtige Lösung:
Modify 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 ändern
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(); } }
Dies ist jetzt die korrekte Ausgabe:
startTime : 1352879531334 update a user 2 endTime : 1352879531334 startTime : 1352879531334 user save.... endTime : 1352879531335
Wenn Sie weniger Code verwenden möchten, können Sie direkt eine anonyme Klasse schreiben:
Paket com.yixi.proxy;
import java .lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
öffentliche Klasse Testen Sie {
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, Object[] Method args)
throws Throwable {
startTime : " +System.currentTimeMillis());
Object obj = method.invoke(usi, args);
System.out.println("endTime : " +System.currentTimeMillis ());
return obj;
Schauen wir nach unten!
Für die (meiner Meinung nach) wichtigste Aufrufmethode schauen wir uns an, was das JDK sagt:
Proxy – die Proxy-Instanz, auf der die Methode aufgerufen wird? Was bedeutet dieser Satz? Schauspielerei? Methode ist die Proxy-Methode? Sollte meine Methode zum Ausführen des Proxys dann nicht Object obj = method.invoke(proxy, args); sein? Ich habe mich damals nicht umgedreht und bin zu Google gegangen, konnte aber keine Inspiration finden. Ich dachte, ich sollte mir lieber den Quellcode ansehen.
Öffnen Sie den Quellcode der Proxy-Klasse und finden Sie heraus, welche Art von Konstruktionsmethode es gibt:
invoke Object invoke(Object proxy, Method method, Object[] args) throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 参数: proxy - 在其上调用方法的代理实例 method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。 args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。Mein erster Gedanke ist, dass Proxy intern die folgende Anweisung aufruft:
Weil Sie die Aufrufmethode aufrufen müssen, um die entsprechende Methode auszuführen
protected InvocationHandler h; protected Proxy(InvocationHandler h) { this.h = h; }Schauen wir uns das zuerst an
在这里你就会发现貌似有点感觉了:当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 Dynamic Proxy-Implementierungsmethode des Proxy-Modus相关文章请关注PHP中文网!