当前位置:  编程技术>java/j2ee

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

    来源: 互联网  发布时间:2014-10-21

    本文导语:  今天一个偶然的机会我突然想看看JDK的动态代理,因为以前也知道一点,而且只是简单的想测试一下使用,使用很快里就写好了这么几个接口和类:接口类:UserService.java 代码如下:package com.yixi.proxy;public interface UserService {    p...

今天一个偶然的机会我突然想看看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方法吧,
我们先来看下这个

在这里你就会发现貌似有点感觉了:当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代理模式与动态代理模式详解
  • Python设计模式之代理模式实例
  • 代理角色java设计模式之静态代理详细介绍
  • c# 代理模式
  • C#中利用代理实现观察者设计模式详解
  • 基于Java的代理设计模式
  • C++设计模式之代理模式
  • *** Linux下装Kingate软件做代理,如何能实现同时代理两个网段?
  • 如何通过http代理实现socks代理?
  • Linux作透明代理服务器,双网卡,要求使用该代理的用户绑定ip,如何实现?
  • 求在freebsd+Squid下实现pc上网的透明代理的实现方法!给出具体配置方法的高分谢!
  • 虚拟机下安装了linux,要做代理服务器,是否无法实现啊
  • linux9.0下可否实现多个域名邮箱的转发代理设置
  • redhat9+ADSL+IPTALBE局域网上网,如何完全实现www,SMTP,pop3代理?大侠帮忙呀!!!
  • 能否用squid2.5-STABLE3 实现透明代理+认证?
  • 如何用iptables实现代理上网?(菜鸟问题)
  • linux双网卡做代理,如何实现两网段互访
  • 急!Ubuntu10.04 apache2和tomcat6 mod_jk 整合 实现apache2的代理问题
  • java实现动态代理示例分享
  • centos配置好了vpn,如何实现高效匿名代理
  • Java动态代理实现AOP
  • squid 2.6 实现透明代理
  • java实现动态代理方法浅析
  • java利用反射实现动态代理示例
  • 深入Ajax代理的Java Servlet的实现详解
  • C#自动设置IE代理服务器(翻墙软件)代码实现
  • 怎样用用iptables+squid实现透明代理?UP无分。
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 正向代理与反向代理概念及其区别
  • sql server 代理(已禁用代理xp)的解决方法
  • 代理服务器原理,功能及作用详细介绍
  • 代理、反向代理究竟是什么意思?
  • 上网代理goagent 3.1.18 正式版下载及简易安装部署教程
  • 用 Linux 做的代理服务器,不能通过代理看外网的电影
  • Spring 2.0 AOP概念及AOP代理,功能和目标
  • 在linux(redhat) 下如何设置代理,我们上网都要用代理的!谢谢了!
  • linux下对于不支持代理的http或是ftp软件有办法使用代理吗
  • squid+nat做透明代理无法控制代理的问题
  • java 静态代理 动态代理深入学习
  • java代理 jdk动态代理应用案列
  • Linux怎么设置代理的问题
  • linux机如何设置代理
  • 救命!Mozilla不能通过代理上网了!!!
  • 代理工具 ProxyChains
  • 如何使用代理访问CVS?
  • linux怎么访问windows2000的代理?
  • 请教一个linux下通过代理上网的问题!
  • Linux系统下如何使用windows主机的代理服务器上网?
  • Chrome代理切换插件 Switchy!


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3