Maison  >  Article  >  Java  >  Méthode d'implémentation de proxy dynamique Java en mode proxy

Méthode d'implémentation de proxy dynamique Java en mode proxy

高洛峰
高洛峰original
2017-02-07 13:49:191320parcourir

Aujourd'hui, par hasard, j'ai eu envie de jeter un œil au proxy dynamique du JDK, car je le connaissais un peu avant, et je voulais juste tester son utilisation. J'ai écrit plusieurs interfaces et classes en peu de temps. :
Classe d'interface : UserService.java

package com.yixi.proxy;
public interface UserService {
    public int save() ;
    public void update(int id);
}

Classe d'implémentation : 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);
    }
}

Ensuite, j'ai rapidement écrit l'InvocationHandler que je voulais : La fonction de ceci est C'est très simple d'enregistrer l'heure de début et l'heure de fin d'exécution de la méthode
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;
    }
}

Une fois tous les préparatifs terminés, il est bien sûr temps de commencer à écrire des tests !
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();
    }
}

Exécutez joyeusement, mais cela ne vous donne pas la face. Le résultat est une exception qui remplit l'écran :

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. . L'exception TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) nous indique clairement que le problème se situe dans la ligne 12 de TimeInvocationHandle : c'est-à-dire

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;
    }
Il n'y a rien de mal dans la méthode ! Parce que la méthode Invoke() semble fournir tous les paramètres requis par method.invoke(Object, Object[]), nous l'utiliserons naturellement. Si vous pensez vraiment de cette façon, alors vous avez trompé le JDK. un piège, regardons d'abord la bonne façon d'écrire. Au cas où certains étudiants ne seraient pas d'humeur à lire ce qui suit, donnez-moi au moins la bonne solution :

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;
    }
}

Modifier 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();
    }
}

Voici le résultat correct maintenant :

startTime : 1352879531334
update a user 2
endTime : 1352879531334
startTime : 1352879531334
user save....
endTime : 1352879531335

Si vous souhaitez utiliser moins de code, vous pouvez écrire directement une classe anonyme :
package com.yixi.proxy;
import java .lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
classe publique 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 Ensure (Object proxy, method, Object[] Method args)
lance Throwable {
startTime : " System.currentTimeMillis());
Object obj = method.invoke(usi, args);
System.out.println("endTime : " System.currentTimeMillis() );
Return obj ;
                        }                                            voke(target,args) Le premier paramètre dans ; Le gestionnaire a-t-il besoin d'un paramètre de proxy d'objet ? Regardons en bas !
Pour la méthode d'invocation la plus importante (à mon avis), jetons un œil à ce que dit le JDK :


proxy - l'instance proxy sur laquelle la méthode est appelée ? Que signifie cette phrase ? par intérim? La méthode est-elle la méthode proxy ? Alors ma méthode pour exécuter le proxy ne devrait-elle pas être Object obj = method.invoke(proxy, args);? Je ne me suis pas retourné à ce moment-là, je suis allé au groupe de discussion et je suis allé sur Google mais je n'ai trouvé aucune inspiration. J'ai pensé que je ferais mieux de regarder le code source et peut-être que je pourrais voir quelque chose !
Ouvrez le code source de la classe Proxy et découvrez quel type de méthode de construction il existe :

Mettez InvocationHandler comme paramètre de la méthode de construction Proxy.... Alors à quoi ça sert le InvocationHandler pour ? Existe-t-il un lien avec la méthode Invocation() dans InvocationHandler ?
invoke
Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 
参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
Ma première pensée est que Proxy appellera l'instruction suivante en interne :


Parce que vous devez appeler la méthode d'invocation pour exécuter la méthode correspondante

protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
    this.h = h;
    }
Jetons un coup d'œil à cela d'abord <.>

Méthode dimplémentation de proxy dynamique Java en mode proxy

在这里你就会发现貌似有点感觉了:当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需要完成的业务。

更多Méthode dimplémentation de proxy dynamique Java en mode proxy相关文章请关注PHP中文网!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn