代理模式

静态代理

以下是一个类代表坦克,接口有一个方法代表坦克移动

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();

    }
posted @ 2021-07-22 18:36  刚刚好。  阅读(28)  评论(0编辑  收藏  举报