在实际的项目开发中,会大量的用到代理模式。这一设计模式又与面向切面编程(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
中的speedUp
或slowDown
args是调用method时传进的参数
2.另外一件事是通过构造方法,将被代理对象传进来。这样就可以将其传给method对象的invoke方法,来执行真实对象的相应方法了
由于我们需要在加速前和减速后做有关处理,因此method.invoke
放在中间执行。
同时InvocationHandler
的invoke
方法返回值应与实际调用方法返回值一致,因此将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();
}
}
Proxy
的newProxyInstance
方法接收三个参数:
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
即可,不需要再实现同样的逻辑了。