zoukankan      html  css  js  c++  java
  • 初探代理模式

    实际上已经不是初探了,早在学习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一个具备权限管理的项目?简单搜索了一下,貌似网上有人也用过这样的思路,等哪天有心情可能继续探索一下这部分内容。

  • 相关阅读:
    【计算机网络】第四章 网络层(4)
    【计算机网络】第四章 网络层(3)
    【计算机网络】第四章 网络层(2)
    grunt/gulp和browserify / webpack
    JavaScript事件机制
    css基础
    javascript事件代理(delegate)原理解析
    前端学习资源
    Ubuntu14.04安装chrome
    防止表单自动填充用户名和密码
  • 原文地址:https://www.cnblogs.com/wxdmw/p/13711971.html
Copyright © 2011-2022 走看看