[Java] Proxy在实际编码中的应用_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > [Java] Proxy在实际编码中的应用

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

 2012/3/6 13:51:25  阿男bluedash  程序员俱乐部  我要评论(0)
  • 摘要:Java的类反射机制中包括了动态代理技术,其核心设计围绕InvocationHandler接口和Proxy类。下面给出一个例子:publicinterfaceCar{publicvoiddescription();}我们定义一个Car接口,然后实现它:publicclassBigCarimplementsCar{publicvoiddescription(){System.out.println("BigCar");}}一般情况下,我们可以这样使用它:Carcar=newBigCar()
  • 标签:Java 应用
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 {
        return method.invoke(delegate, args);
    }


}


使用方法如下:
       
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());
        Object returnObj = null;
        try {
            returnObj = method.invoke(delegate, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            System.out.println("after method: " + method.getName());
        }
        return returnObj;
    }
}


此时再执行:

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
发表评论
用户名: 匿名