代理模式之Java动态代理实现方法

2016-02-19 11:43 1 1 收藏

下面图老师小编跟大家分享代理模式之Java动态代理实现方法,一起来学习下过程究竟如何进行吧!喜欢就赶紧收藏起来哦~

【 tulaoshi.com - 编程语言 】

今天一个偶然的机会我突然想看看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.Proxy;
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 {
                        System.out.println("startTime : " +System.currentTimeMillis());
                        Object obj = method.invoke(usi, args);
                        System.out.println("endTime : " +System.currentTimeMillis());
                        return obj;
                    }
                });
        u.update(2);
        u.save();
    }
}
既然method.invoke(target,args);中第一个参数是传入的是目标对象 那么invocationHandler的Invoke方法要个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)的实例中。

proxy - 在其上调用方法的代理实例 ? 这句话是什么意思呢? 代理? method是代理的方法? 那我执行代理的method不是就应该是Object obj = method.invoke(proxy, args);吗? 当时我也没转过弯来,去讨论群,去google都没找到什么灵感,想想还是这个看看源码吧 也许能看到点什么!
打开Proxy类的源码发现有怎么一个构造方法:
代码如下:

protected InvocationHandler h;

protected Proxy(InvocationHandler h) {
    this.h = h;
    }

把InvocationHandler作为Proxy的构造方法的参数....那它要InvocationHandler干什么用呢?跟InvocationHandler中的invoke()方法有什么联系吗?
我第一个想到的是Proxy内部会调用下面的语句:
代码如下:

h.invoke(this,methodName,args);

因为总得去调用invoke方法才能执行相应的method方法吧,
我们先来看下这个

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

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

文采不行!能力有限!希望大家指正...

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

来源:https://www.tulaoshi.com/n/20160219/1598360.html

延伸阅读
标签: PHP
作者:Binzy 来源:超越PHP 一、 摘要 本文简单阐述了Proxy模式及具体说明了如何在PHP4中实现动态代理, 本文只是给出了一个实现的方法的原型. 由于水平有限, 有任何意见和建议请反馈给Binzy [ Binzy at JustDN dot Com ]. 二、 概述 在我们开始实现Dynamic Proxy之前, 也许我们应该先了解一下什么是Proxy和它有什么用. 下面是一...
目前整个开发社区对AOP(Aspect Oriented Programing)推崇备至,也涌现出大量支持AOP的优秀Framework,--Spring, JAC, Jboss AOP 等等。AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨,OOP还没有学通呢,又来AOP。本文不是要在理论上具体阐述何为AOP, 为何要进行AOP . 要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通...
本来想上周末没能用DELPHI实现动态代理就算了,可是这几天却始终放不下这个想法,这实在是一个太美妙的想法了。而且在认真看了VCL对SOAP的实现后,现在至少有九成的把握可以实现这样一个动态代理。 那么动态代理有什么用? 这要先从GoF的Proxy模式说起。 假设有下面这样一个接口及其实现: 现在,如果你是这个接口的用户...
数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的连接数据库对服务性能来讲是一个瓶颈,使用缓冲池技术可以来消除这个瓶颈。我们可以在互联网上找到很多关于数据库连接池的源程序,但是都发现这样一个共同的问题:这些连接池的实现方法都不同程度地增加了与使用者之间的耦合度。很多的连接池都要求用户通过其规定的方法获取数据库...
我的站点上的所有文件都是静态html,为了进一步的加快网页速度,于是使用squid实现反向代理缓存功能。 @ 安装squid 为了偷懒就直接使用ports安装,因为我将所有的服务器系统都安装在/s分区中,所以设置了PREFIX为/s/squid # cd /usr/ports/www/squid # setenv PREFIX /s/squid # make install clean 安装完成后编辑/s/squid/etc/squid...

经验教程

109

收藏

67
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部