[Java] Proxy在实际编码中的应用

标签: java proxy 编码 | 发表时间:2012-03-03 00:08 | 作者:
出处:http://www.iteye.com
Java的类反射机制中包括了动态代理技术,其核心设计围绕InvocationHandler接口和Proxy类。下面给出一个例子:

public interface Car {

    public void description();
}


我们定义一个Car接口,然后实现它:

public class BigCar implements Car {

    public void description() {
        System.out.println("BigCar");
    }

}


一般情况下,我们可以这样使用它:

Car car = new BigCar();
car.description();


程序会输出:

BigCar


如果我们要用Proxy来做,首先需要一个实现了InvocationHandler的代理类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CarProxy implements InvocationHandler {

    private Object delegate;

    public Object newInstance(Object delegate) {
        this.delegate = delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        method.invoke(delegate, args);
        return null;
    }


}


使用方法如下:
       
CarProxy cp = new CarProxy();
Car car = (Car) cp.newInstance(BigCar.class.newInstance());
car.description();


执行程序,同样会输出BigCar。但代码似乎复杂了很多,这样做有什么意义呢?在我接触过的开源项目中,使用Proxy一般目的有两个:一是为了在程序逻辑执行时插入其它逻辑(比如添加日志或是改写参数传递);二是为了改变程序逻辑。

首先从第一种情况说起。注意到InvocationHandler里面的invoke方法,整个Proxy机制的核心就是invoke方法。invoke方法接收Method,即接口方法,我们在这个层面就可以插入一些额外的逻辑,比如添加日志:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CarProxy implements InvocationHandler {

    private Object delegate;

    public Object newInstance(Object delegate) {
        this.delegate = delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        System.out.println("before method: " + method.getName());

        try {
            method.invoke(delegate, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            System.out.println("after method: " + method.getName());
        }
        return null;
    }
}


此时再执行:

CarProxy cp = new CarProxy();
Car car = (Car) cp.newInstance(BigCar.class.newInstance());
car.description();


程序输出如下:

before method: description
BigCar
after method: description


下面我们看看第二种情况,即改写代码逻辑:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CarProxy implements InvocationHandler {

    private Object delegate;

    public Object newInstance(Object delegate) {
        this.delegate = delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        if (method.getName().equals("description")) {
            System.out.println("Caught by proxy.");
        }

        return null;
    }
}


此时执行:

CarProxy cp = new CarProxy();
Car car = (Car) cp.newInstance(BigCar.class.newInstance());
car.description();


程序输出如下:

Caught by proxy.


可见car.description的逻辑已经被Proxy拦到并改写了。这些用法在框架级代码中都很普遍,比如RESTEasy的ClientProxy[1]。

此外,我们在CarProxy中通过:

private Object delegate;


保留了它代理的实现类BigCar。

实际上这不是必须的,而Proxy框架提供给我们的是更灵活的设计,实际上一个代理可以代理多个类。

下面给出个例子,首先定义两个不同的接口及其实现类:

public interface Flavor {
    public void taste();
}

public class Spicy implements Flavor {
    public void taste() {
        System.out.println("Spicy");
    }
}

public interface Color {
    public void show();
}

public class Red implements Color {
    public void show() {
        System.out.println("Red");
    }
}


然后我们用一个InvocationHandler同时代理两个接口[2]:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MultipleProxy implements InvocationHandler {

    private Class[] interfaces;
    private Object[] delegates;

    public MultipleProxy(Class[] interfaces, Object[] delegates) {
        this.interfaces = (Class[]) interfaces.clone();
        this.delegates = (Object[]) delegates.clone();
    }

    public Object invoke(Object proxy, Method m, Object[] args)
            throws Throwable {
        Class declaringClass = m.getDeclaringClass();


        for (int i = 0; i < interfaces.length; i++) {
            if (declaringClass.isAssignableFrom(interfaces[i])) {
                try {
                    return m.invoke(delegates[i], args);
                } catch (InvocationTargetException e) {
                    throw e.getTargetException();
                }
            }
        }

        return methodNotFound(proxy, m, args);

    }

    protected Object methodNotFound(Object proxy, Method m,
                                    Object[] args)
            throws Throwable {
        throw new InternalError("unexpected method dispatched: " + m);
    }

}


使用这个代理:

Class[] proxyInterfaces = new Class[]{Color.class, Flavor.class};
Object obj = Proxy.newProxyInstance(Color.class.getClassLoader(),
        proxyInterfaces,
        new MultipleProxy(proxyInterfaces, new Object[]{new Red(), new Spicy()}));

Color color = (Color) obj;
color.show();

Flavor flavor = (Flavor) obj;
flavor.taste();


程序输出如下:

Red
Spicy



[1] http://grepcode.com/file/repository.jboss.org/maven2/org.jboss.resteasy/resteasy-jaxrs/1.1-RC2/org/jboss/resteasy/client/core/ClientProxy.java

[2] 代码参考自:http://docs.oracle.com/javase/1.3/docs/guide/reflection/proxy.html

已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java proxy 编码] 推荐:

[Java] Proxy在实际编码中的应用

- - ITeye博客
Java的类反射机制中包括了动态代理技术,其核心设计围绕InvocationHandler接口和Proxy类. 我们定义一个Car接口,然后实现它:. 一般情况下,我们可以这样使用它:. 如果我们要用Proxy来做,首先需要一个实现了InvocationHandler的代理类:. 执行程序,同样会输出BigCar.

java 动态代理(Proxy)

- - BlogJava_首页
动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实,代理对象对客户隐藏了实际对象. 动态代理可以对请求进行其他的一些处理,在不允许直接访问某些类,. 或需要对访问做一些特殊处理等,这时候可以考虑使用代理. 目前 Java 开发包中提供了对动态代理的支持,但现在只支持对接口的实现. 主要是通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口.

java编码规范

- - ITeye博客
   总结前期做的几个项目,个人认为代码的规范对团队的协作有着密切的关系. 现将一些常用的约束总结如下,以便今后参阅:. 1、所有的类、属性、方法都遵守以字母和数字为主,尽量不要参与特殊符号如下划线. 其次,除类名开头字母大写外,其他名字都要小写,然后第二个后的单词首字母大写,长度在30个字符以内.

Java/Android编码规范

- - CSDN博客推荐文章
1.        为什么需要编码规范?. 编码规范对于程序员而言尤为重要,有以下几个原因:. l        一个软件的生命周期中,80%的花费在于维护. l        几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护. l        编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码.

MySQL Proxy 0.8.4 发布

- - 开源中国社区最新新闻
MySQL Proxy 0.8.4 发布. 2014-01-10 这是MySQL官方读写分离以及负载均衡工具,上一个版本还是2012-08-20的0.8.3.过了一年半. 国内360基于这个发布了Atlas. MySQL-Proxy是处在你的MySQL数据库客户和服务端之间的程序,它还支持嵌入性脚本语言 Lua.

Java写xml文件的编码问题

- - CSDN博客推荐文章
最近项目中需要生成xml格式的配置文件,用的是 javax.xml.transform.Transformer 类中提供的transform方法,在本地执行没问题,但是一旦把工程部署到Tomcat下运行,就会出现中文乱码的现象,纠结了许久,在大神的帮助下终于解决了. 有篇文章其实已经讲的很清楚了,链接如下:.

Java编码规范(Google版本)

- - 研发管理 - ITeye博客
本文摘自:http://hawstein.com/posts/google-java-style.html. 这份文档是Google Java编程风格规范的完整定义. 当且仅当一个Java源文件符合此文档中的规则, 我们才认为它符合Google的Java编程风格. 与其它的编程风格指南一样,这里所讨论的不仅仅是编码格式美不美观的问题,同时也讨论一些约定及编码标准.

java编码规范及优化总结

- - CSDN博客编程语言推荐文章
1991 年Sun公司的James Gosling(詹姆斯·高斯林)等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒、PDA等的微处理器. 1994年将Oak语言更名为Java. 二、 Java的三种技术架构. J2EE:Java PlatformEnterprise Edition,开发企业环境下的应用程序,主要针对web程序开发;.

为MetroTwit设置Http Proxy

- stranger - iGFW
MetroTwit无疑是Windows平台上让人赏心悦目的Twitter Client. 但没有为用户提供Http Proxy或API Proxy,这给很多人带来了使用上的不便. 下面我尝试了两种为MetroTwit设置Http Proxy的方法,效果都还不错. 为Windows系统设置全局的Proxy,也就是在IE中设置Proxy.

Dynamic Proxy (动态代理)

- - CSDN博客推荐文章
动态代理主要有一个 Proxy类 和一个 InvocationHandler接口. 真实主题角色(实现了抽象主题接口). 动态代理主题角色(实现了 InvocationHandler接口,并实现了 invoke()方法). Proxy 要调用 newProxyInstance方法. 1.抽象主题角色 SubjectDemo.java.