Java中的动态代理

来源:互联网 发布:乐视自行车软件 编辑:程序博客网 时间:2024/05/22 15:25

在实际的项目开发中,会大量的用到代理模式。这一设计模式又与面向切面编程(AOP)紧密相关。
Java中可以通过静态代理或动态代理两种方式实现代理模式。其中静态代理容易理解,但由于需要编写大量代理类及代理方法代码,非常不利于维护;而动态代理的代理类在运行时生成,也不用编写大量重复性代码,相比静态代理有很大的优势。

动态代理涉及一个重要的接口InvocationHandler以及一个重要的类Proxy,实现动态代理的步骤大致为:
1.抽象出公共的接口类,用于代理。
2.实现公共接口类,作为被代理的对象类。
3.实现InvocationHandler接口,并与公共接口类绑定。
4.通过Proxy生成动态代理的对象。
5.通过调用代理对象的接口方法,操控被代理对象。

下面通过一个例子来看看动态代理的具体实现:
首先我们定义一个ICar接口,抽象出汽车的两个行为——加速speedUp与减速slowDown:

public interface ICar {    void speedUp();    void slowDown();}

接下来实现一个具体的汽车类Benz,在实现方法中简单的进行打印输出:

public class Benz implements ICar {    @Override    public void speedUp() {        System.out.println("Benz speedUp");    }    @Override    public void slowDown() {        System.out.println("Benz slowDown");    }}

假设有这样的需求:在加速前,减速后进行纪录当前时间,记录车的时速等操作,
这些都与汽车加速/减速本身的机械动作(业务)无关,因此直接在Benz中添加大量这样的代码,会非常不利于维护。
在这样情况下使用代理模式就可以避免侵入业务逻辑。对于动态代理的模式,在这时我们先实现InvocationHandler接口类:

public class CarHandler implements InvocationHandler {    private final ICar target;    public CarHandler(ICar car) {        target = car;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if (method.getName().equals("speedUp")) {            System.out.println("CarHandler before car speedUp");        }        Object result = method.invoke(target, args);        if (method.getName().equals("slowDown")) {            System.out.println("CarHandler after car slowDown");        }        return result;    }}

这个类主要做了两件事情:
1.实现invoke方法,代理操作的主要逻辑在这里实现。在代理对象执行具体某个方法时,会调用到这里。
这个方法中有三个参数:
proxy是实际的代理对象,一般情况下不会使用。特别是在调用method的invoke方法时,不要传这个参数,否则代理对象执行方法还会再执行进来,直接报stackOverFlow错误……
method代表通过代理对象调用的接口函数,例如ICar中的speedUpslowDown
args是调用method时传进的参数
2.另外一件事是通过构造方法,将被代理对象传进来。这样就可以将其传给method对象的invoke方法,来执行真实对象的相应方法了

由于我们需要在加速前和减速后做有关处理,因此method.invoke放在中间执行。
同时InvocationHandlerinvoke方法返回值应与实际调用方法返回值一致,因此将method.invoke的结果返回给上层。

至此准备工作就完成了,下面来看如何使用这个动态代理:

public class TestMain {    public static void main(String[] args) {        ICar benzCar = new Benz();  // 首先生成被代理对象        CarHandler carHandler = new CarHandler(benzCar);  // 创建InvocationHandler对象,将被代理对象传入绑定        ICar target = (ICar) Proxy.newProxyInstance(                benzCar.getClass().getClassLoader(),                benzCar.getClass().getInterfaces(),                carHandler);  // 通过Proxy类的newProxyInstance静态方法,生成代理对象        target.speedUp();  // 通过代理对象进行操作        System.out.println();        target.slowDown();    }}

ProxynewProxyInstance方法接收三个参数:
1.被代理对象的ClassLoader
2.被代理对象所实现的接口。在本例中即ICar
3.InvocationHandler实现类
通过这个静态方法返回代理对象,然后再通过代理对象调用各公共接口,触发invoke中编写的代理逻辑。
运行程序后输出:

CarHandler before car speedUpBenz speedUpBenz slowDownCarHandler after car slowDown

通过这个例子可以看到使用代理模式,不需要实现每个代理方法(设想ICar有100个接口的情况)。同时代理对象在运行时动态生成,提高了灵活性。

这可以通过一个扩展例子体现:
新需求要求所有接口调用时都要记录日志,便于后期追踪问题。在静态代理模式下,我们不得不为每个接口方法添加log函数,有大量重复性工作。在动态代理的模式下,可以通过定义InvocationHandler的公共类来解决。

首先定义一个基础的CommonHandler类,实现代理的通用逻辑:

public abstract class CommonHandler implements InvocationHandler {    private final Object target;    public CommonHandler(Object obj) {        target = obj;    }    public Object getTarget() {        return target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("CommonHandler print log before method invoke");        return null;    }}

可以看到在invoke最开始添加了日志逻辑。由于不同代理类的逻辑在子类处理,为了避免过早调用实际方法,这里直接返回空值,并将类声明为抽象类。
另外添加了getTarget方法,供外部访问target对像,同时target对象修改为Object类型。
接下来对CarHandler进行修改:

public class CarHandler extends CommonHandler {    public CarHandler(ICar car) {        super(car);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        super.invoke(proxy, method, args);        if (method.getName().equals("speedUp")) {            System.out.println("CarHandler before car speedUp");        }        Object result = method.invoke(getTarget(), args);        if (method.getName().equals("slowDown")) {            System.out.println("CarHandler after car slowDown");        }        return result;    }}

CarHandler继承于CommonHandler,在invoke中调用父类方法记录日志,再执行原有逻辑。同时去除了被代理对象的引用,使用父类的getTarget方法获取。

再次运行程序,输出如下:

CommonHandler print log before method invokeCarHandler before car speedUpBenz speedUpCommonHandler print log before method invokeBenz slowDownCarHandler after car slowDown

在每个被代理的方法前都添加了日志。如果其他类型的代理对象需要这一逻辑,直接继承CommonHandler即可,不需要再实现同样的逻辑了。

0 0