Heim  >  Artikel  >  Java  >  Java Dynamic Proxy-Implementierungsmethode des Proxy-Modus

Java Dynamic Proxy-Implementierungsmethode des Proxy-Modus

高洛峰
高洛峰Original
2017-02-07 13:49:191321Durchsuche

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:

Geben Sie InvocationHandler als Parameter der Proxy-Konstruktionsmethode ein ... Was wird also verwendet? der InvocationHandler für? Gibt es eine Verbindung mit der invoke()-Methode in InvocationHandler?
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

Java Dynamic Proxy-Implementierungsmethode des Proxy-Modus

在这里你就会发现貌似有点感觉了:当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方法的是相应的代理方法的返回值,
所以在invoke方法返回null的时候代理的update方法接收到返回值是null, 而它本来就是返回void 所以没有报异常, 而代理save必须返回int型的数值 我们这返回的还是null,JVM无法将null转化为int型 所以就报了异常了
这样解释就能解释通了,也能相对证明前面的猜测。

InvocationHandler中invoke方法中第一个参数proxy貌似只是为了让Proxy类能给自己的InvocationHandler对象的引用调用方法时能传入代理对象proxyClass的引用,来完成proxyClass需要完成的业务。

更多Java Dynamic Proxy-Implementierungsmethode des Proxy-Modus相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn