初探代理模式

实际上已经不是初探了,早在学习spring的时候就接触过代理模式,可惜当时没怎么搞明白动态代理,也没太重视这一块的知识,最近买了一本新书,里面恰好讲到了这一块的知识,于是,就认真学习了一遍,后面讲解原理的部分还没有学完,为了防止忘记,先将前面部分的偏应用的部分记录一下。

代理分为两种:静态代理和动态代理

我的理解就是所谓静态代理就是写死的代理,并不灵活,适应性比较差,但是,编写起来更为简单也更易于理解。

而动态代理从代码难易程度上来看要比静态代理复杂,但是,它的适应性也更好,更为灵活。

代理的作用是保护目标对象和增强目标对象。目标对象也就是实际被代理的对象,最终实际上还是调用了它,但不再是直接去调用它的方法,而是直接调用代理,由代理去调用目标对象的方法。

所以对目标对象起到保护作用这一点似乎并不难理解,记得我刚开始接触到代理模式的时候,总是不大能理解所谓的增强目标对象是什么意思?

经过这一次的学习,对于这个概念也有了一些体会,所谓的增强指的是在调用原本方法的前后增加其他的逻辑,使原本方法的功能更加强大。

比如这样:

public void 代理方法() {
      //记录日志
      log();       
      //被代理对象的方法,查询xxx数据
      xxx.select();
}

代理方法在调用原本对象的方法时,“动了些手脚”,这就是所谓的增强。

编写简单的动态代理:

首先,编写一个被代理对象的接口(这个必须有)

interface Client {

    public void song();
}

然后,被代理对象的实现类去实现这个接口

class ClientImpl implements Client {

    public void song() {
        System.out.println("----------");
    }
}

现在万事俱备,需要一个代理类来代理我们写好的被代理类

public class ProxyTest implements InvocationHandler {

    private Object any;

    public ProxyTest (Object any) {
        this.any = any;
    }

    private void before() {
        System.out.println("one day");
    }

    private void after() {
        System.out.println("maybe she'll stay");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(this.any, args);
        after();
        return obj;
    }
}

步骤实际上并不复杂,首先需要实现InvocationHandler接口,然后提供一个传入被代理对象的方法,并且随你的心意去增强被代理对象的方法即可。

测试代码如下:

class Try {
    public static void main(String[] args) {
        Class<?> clazz = ClientImpl.class;
        Client clientPro = (Client)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new ProxyTest(new ClientImpl()));
        clientPro.song();
    }
}

想要代理哪个对象就将它的 .class 取出来,然后主方法内第二行略有些长的代码负责生成代理对象,其中传入的最后一个参数是实现了InvocationHandler接口的对象,我利用构造器将被代理对象一并传入。

然后调用代理的方法即可。

注意,此处代理对象的构造方法形参为object,所以不一定非要传入当前定义的被代理对象,也可以传入其他对象,也就是所谓的“动态”,before和after两个方法就是在原本方法的前后增加了新的逻辑,也就是所谓的“增强”。

但是,我觉得这样来实现最后还需要强转类型什么的,比较麻烦,所以,稍微简化了一下。

public class ProxyImprove<T> implements InvocationHandler {

    private T obj;

    public ProxyImprove() {

    }

    public T setObj(T obj) {
        this.obj = obj;
        Class clazz = obj.getClass();
        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    private void before () {
        System.out.println("before");
    }

    private void after () {
        System.out.println("after");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object o = method.invoke(obj, args);
        after();
        return o;
    }
}

class Test {
    public static void main(String[] args) {
        ProxyImprove<Client> improve = new ProxyImprove<>();
        Client client = improve.setObj(new ClientImpl());
        client.song();
    }
}

其实我觉得如果invoke中的逻辑相对固定的话,还可以这样封装一下(没测试过):

public interface MyHandler extends InvocationHandler {

    @Override
    default Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        before();
        Object obj = method.invoke(get(), args);
        after();
        return obj;
    }

    public void set(Object obj);
    public Object get();
    public void before();
    public void after();
}

由于前一段时间接触了一些安全框架,我突发奇想动态代理这东西能否用来build一个具备权限管理的项目?简单搜索了一下,貌似网上有人也用过这样的思路,等哪天有心情可能继续探索一下这部分内容。

posted @ 2020-09-22 14:27  无心大魔王  阅读(154)  评论(0编辑  收藏  举报