zoukankan      html  css  js  c++  java
  • 设计模式——代理模式(静态代理和JDK、CGLib动态代理)

    简介

    什么是代理模式?

    代理模式就是多一个代理类出来,代替原对象进行一些操作。比如说租房的中介、打官司的律师、旅行社,他们可以代替我们做一些事情,这就是代理。

    代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
    1. 修改原有的方法来做到改进。但这样违反了“对扩展开放,对修改关闭”的原则。
    2. 采用一个代理类调用原有的方法,且对产生的结果进行控制。这就是代理模式。

    代理模式的分类:

      静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。再程序运行前代理类的.class文件就已经存在了。

      动态代理:在程序运行时用反射机制,动态创建代理类。

    静态代理

    所谓静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public interface Hello {
        public void sayHello(String way);
    }
    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class HelloImpl implements Hello{
        public void sayHello(String way) {
            System.out.println("Hello by " + way);
        }
    }
    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class HelloProxy implements Hello {
        private Hello hello;
        public HelloProxy() {
            hello = new HelloImpl();
        }
        private void sayBefore() { System.out.println("Before.."); }
        private void sayAfter() { System.out.println("After.."); }
        public void sayHello(String way) {
            sayBefore();
            hello.sayHello(way);
            sayAfter();
        }
    }

    代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。HelloProxy 类实现了 Hello 接口,在构造方法中 new 出一个 HelloImpl 类的实例。在 sayHello()方法中调用 HelloImpl 的 sayHello()方法,同时在调用的前后加上 before 与 after 方法。

    测试类:

    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class Test {
        public static void main(String[] args) {
            Hello proxy = new HelloProxy();
            proxy.sayHello("normal");
        }
    }

    输出结果:

    Before..
    Hello by normal
    After..

    以上就是静态代理,下面分析其带来的优缺点:

    优点:

      客户端不必知道实现类(委托类)如何如何,只需要调用代理类即可。

    缺点:

    • 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。但这样出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也要实现这个方法。这显然增加了代码的复杂度。
    • 代理对象只服务于一种类型的对象,如果要服务多类型的对象,那就要对每种对象都进行代理。静态代理子啊程序规模稍大是就无法胜任了。

    JDK代理——接口级别代理

    package Proxy;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class DynamicProxy implements InvocationHandler{
        private Object target;
        public DynamicProxy(Object target) { this.target = target; }
        @SuppressWarnings("unchecked")
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    this
            );
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            sayBefore();
            Object result = method.invoke(target, args);
            sayAfter();
            return result;
        }
        private void sayBefore() { System.out.println("before..."); }
        private void sayAfter() { System.out.println("after..."); }
    }

    JDK 提供的 Proxy 类的工厂方法 newProxyInstance 去动态地创建一个 Hello 接口的代理类。

    Proxy.newProxyInstance:

    参数:

    • loader - 定义代理类的类加载器
    • interfaces - 代理类要实现的接口列表
    • h - 指派方法调用的调用处理程序(每个代理实例都具有一个关联的调用处理程序,调用代理实例的方法时,将对方法的调用指派到它的调用处理程序的 invoke 方法)

    返回:

    一个带有代理类的指定调用处理程序的代理实例,它由指定类加载器定义,并实现指定的接口。

    测试类:

    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class Test {
        public static void main(String[] args) {
            DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
            Hello hello = dynamicProxy.getProxy();
            hello.sayHello("JDK");
        }
    }

    动态代理相比静态代理,接口变了,动态代理类不需要改变,而静态代理类不仅需要改变实现类,代理类也需要修改。

    但如果要代理一个没有接口的类,JDK 动态代理就用不上了,这就引出了 CGLib 代理。

    CGLib代理——方法级别代理

    Spring、Hibernate 框架都是用了它,它是一个在运行期间动态生成字节码的工具,也就是动态生成代理类。

    package Proxy;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    //Created by zhengbinMac on 2017/2/19.
    public class CGLibProxy implements MethodInterceptor {
        // 单例模式
        private static CGLibProxy instance = new CGLibProxy();
        private CGLibProxy() {}
        public static CGLibProxy getInstance () { return instance; }
        public <T> T getProxy(Class<T> cls) {
            return (T) Enhancer.create(cls, this);
        }
        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            sayBefore();
            Object result = methodProxy.invokeSuper(obj, objects);
            sayAfter();
            return result;
        }
        private void sayBefore() { System.out.println("before..."); }
        private void sayAfter() { System.out.println("after..."); }
    }

    CGLib 给我们提供的是方法级别的代理,也可以理解为对方法的拦截。通过直接调用 proxy 的 invokeSuper 方法,将被代理的对象 obj 以及方法的参数 objects 传入其中即可。

    测试类:

    package Proxy;
    //Created by zhengbinMac on 2017/2/19.
    public class Test {
        public static void main(String[] args) {
            Hello helloCGLib = CGLibProxy.getInstance().getProxy(HelloImpl.class);
            helloCGLib.sayHello("CGLib");
        }
    }
  • 相关阅读:
    js 获取当前时间
    html5拨打电话及发短信
    ::before和::after伪元素的使用
    vue单页面应用刷新网页后vuex的state数据丢失问题以及beforeunload的兼容性
    CSS3径向渐变实现优惠券波浪造型
    iOS 幻灯片的自动循环滚动
    iOS 编译正常,但无法运行到真机和模拟器上,Choose a destination with a supported architecture in order to run on this device.
    iOS webView抓取改变js的alertView
    iOS 创建单例的方法
    webView图片点击可以实现预览效果
  • 原文地址:https://www.cnblogs.com/zhengbin/p/6514045.html
Copyright © 2011-2022 走看看