代理模式
静态代理
以下是一个类代表坦克,接口有一个方法代表坦克移动
public class Tank implements MoveService{
@Override
public void move() {
System.out.println("Tank start move ");
}
}
interface MoveService{
void move();
}
现在如果我想记录坦克的移动时间,应该怎么做呢?
最朴素的想法应该是 我们在move()方法中 添加对时间的记录即可
public class Tank implements Moveservice{
@Override
public void move() {
long start = System.currentTimeMillis();
System.out.println("Tank start move ");
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
但是 如果我们Tank类已经写死了怎么办?
此时我们可以采用代理类!
class TankProxy implements MoveService{
Tank tank;
TankProxy(Tank tank){
this.tank=tank;
}
@Override
public void move() {
long start = System.currentTimeMillis();
tank.move();
long end = System.currentTimeMillis();
}
}
TankProxy就是一个代理类,我们可以采用聚合的方式,在Tank类完成相关操作时 加入我们的一些操作!
但是 此时还有一些问题:如果我们有多个类都实现了MoveService接口怎么办? 很简单 我们把代理对象改为MoveService即可,此时所有实现MoveService接口的类都能被代理!我们甚至可以实现嵌套代理
class TankProxy implements MoveService{
MoveService moveService;
TankProxy(MoveService moveService){
this.MoveService= moveService;
}
@Override
public void move() {
long start = System.currentTimeMillis();
moveService.move();
long end = System.currentTimeMillis();
}
}
动态代理(jdk)
经过上边的静态代理,我们可以代理实现MoveService接口的对象,如果我们想要代理任意对象,任意方法呢?
这就用到我们的动态代理。
动态代理顾名思义,就是不需要我们手写代理类,而是可以动态生成代理类
public class Tank implements MoveService{
@Override
public void move() {
System.out.println("Tank start move ");
}
}
public interface MoveService{
void move();
}
public static void main(String[] args) {
Tank tank = new Tank();
//这里生成的就是我们的代理类
MoveService moveService = (MoveService) Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{MoveService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//这里实际返回的是空值
Object invoke = method.invoke(tank, args);
long end = System.currentTimeMillis();
return invoke;
}
});
//这里封装的很好,我们直接执行对应接口的方法即可
moveService.move();
}
我们看Proxy.newProxyInstance这个方法,他一共有三个参数,第一个参数为 被代理类的类加载器, 第二个参数为 动态代理类需要继承的接口(必须面向接口!) ,第三个参数为InvocationHandler的实现类 定义了我们需要对这些接口的方法进行怎么样的处理。方法生成的为代理类对象
InvocationHandler的invoke方法中是我们代理类添加的具体操作。我们看下invoke()的三个参数,第一个是生成的代理类的对象,第二个是执行的方法,第三个是方法传入的参数。里边有句method.invoke(被代理类的对象,args) 这个方法的意思等于 被代理类的对象.method(args)
。 比如此时 被代理对象是tank method是move 那么就是tank.move()
我们明明是调用的接口的move()方法 为什么会执行InvocationHandler实现类的invoke方法?
原因很简单,就是在生成动态代理类时,在代理类的move()方法中 实际上就是执行InvocationHandler对象的invoke()方法
显然jdk的动态代理方法的局限性就在于必须面向接口
动态代理(cglib)
需要先导入cglib依赖
public class Tank {
public void move() {
System.out.println("Tank start move ");
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("before");
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("after");
return res;
}
});
Tank car = (Tank) enhancer.create();
Tank.move();
}