Java中的动态代理

14 篇文章 0 订阅

在实际的项目开发中,会大量的用到代理模式。这一设计模式又与面向切面编程(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 speedUp
Benz speedUp

Benz slowDown
CarHandler 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 invoke
CarHandler before car speedUp
Benz speedUp

CommonHandler print log before method invoke
Benz slowDown
CarHandler after car slowDown

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值