为什么需要代理
假设有一个计算器类,类中定义了加,减,乘,除操作,然后我希望在每个方法执行前后打印一些提示信息,比如 “XX方法开始执行。。”,“XX方法结束执行。。”,要做到这一点,普通的解决方式是在每个方法前后加上 System.out.println("XX方法正在执行。。"),System.out.println("XX方法结束执行。。"),这样写起来十分麻烦,如果我们以后要扩充这个类,要加上乘方,开方等方法,还要再加上提示信息,代码及其冗余,也不便于维护。
代理模式可以解决这种囧境,利用这种模式,可以将一些通用的操作抽取出来,比如记录日志,权限管理等等,这些操作在很多地方都能用到,将这些操作单独拿出来,运行时动态加进去,这时我们希望的。
代理分为静态代理:主要通过继承和组合的方式实现,以及下面主要介绍的动态代理。
此外代理模式是Spring框架中AOP的基础,所以理解代理模式非常重要。
使用jdk自带的库来实现动态代理
被代理的类需要实现特定的接口,我们先定义这个接口:
package com.hunan.proxy;
public interface Moveable {
//move方法,参数为移动速度,返回移动的总路程
public double move(double speed);
}
接下来我们写一个Handler为所有实现这个接口的类做代理,上面的接口中声明了move方法,我们希望再执行move方法前后记录日志,利用代理来将日志处理模块提取出来:
package com.hunan.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(Object target) {
super();
this.target = target;
}
/*
* 参数说明:
* proxy 被代理的对象
* method 被代理对象的方法
* args 方法的参数
*
* 返回值:
* Object 被代理的对象的方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 本例仅为演示动态代理,具体的日志处理过程就不具体实现了,仅打印一条提示信息。
System.out.println("开始记录日志。。。");
Object obj = method.invoke(target, args);
System.out.println("日志记录完毕。。。");
return obj;
}
}
注意:代理是代理某个特定接口的实现类,而不是代理某个特定类,所以尽量用Object引用指向对象,使代码有更好的通用性。
第三步实现接口,用线程休眠的方式模拟汽车行驶:
package com.hunan.proxy;
import java.util.Random;
public class Car implements Moveable {
@Override
public double move(double speed) {
System.out.println("汽车行驶中。。。");
int moveTime = new Random().nextInt(3000);
try {
Thread.sleep(moveTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return speed*moveTime;
}
}
最后写个main方法测试一下:
package com.hunan.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new LogHandler(car);
Class<?> cls = car.getClass();
/*
* loader:类加载器
* interface:实现的接口
* h:InvocationHandler实例
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
double distance = m.move(50);
System.out.println("move: " + distance + " meters....");
}
}
运行结果如下:
基于cglib的动态代理
使用cglib来实现动态代理需要引入cglib的依赖包,这个网上很容易下载到。cglib的代理实现方式实际上是基于继承来实现的,它会创建一个被代理类的子类,在子类中添加日志处理等模块并调用父类的方法。这些子类由cglib帮我们管理。
仍然是代理前面的Moveable接口的实现类:
首先要实现MethodInterceptor接口:
package com.hunan.cglibproxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/*
* 拦截所有目标类方法的调用
* obj:目标类的实例
* m: 目标方法的反射对象
* args:方法的参数
* proxy:代理类的实例
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
*/
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("日志开始。。。");
//代理类调用父类方法
Object distance= proxy.invokeSuper(obj, args);
System.out.println("日志结束。。。");
return distance;
}
}
这个enhancer就是用于实现代理的子类了,不过cglib帮我们包装了一下。
接下来写一个Train类实现Moveable接口:
package com.hunan.cglibproxy;
import java.util.Random;
import com.hunan.proxy.Moveable;
public class Train implements Moveable {
@Override
public double move(double speed) {
System.out.println("火车行驶中。。。");
int moveTime = new Random().nextInt(3000);
try {
Thread.sleep(moveTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return speed*moveTime;
}
}
写一个main方法测试一下:
package com.hunan.cglibproxy;
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Train t = (Train)proxy.getProxy(Train.class);
double distance = t.move(100);
System.out.println("火车行驶了: " + distance + "路程。。");
}
}
打印输出如下: