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

      代理(Proxy),顾名思义,就是不用自己去做,而是让别人代替你去做。它在程序开发中起到了非常重要的作用,比如传说中的 AOP(面向切面编程),就是针对代理的一种应用。此外,在设计模式中,针对它也有一个代理模式。

      代理模式分为“静态代理” 和“动态代理” 两种。我们先来看静态代理。

      先来一个HelloWorld 吧。

    package com.hd.proxy;
    
    public interface IHello {
        void sayHello(String name);
    }

      再来一个实现类:

    package com.hd.proxy;
    
    public class HelloImpl implements IHello {
    
        @Override
        public void sayHello(String name) {
            System.out.println("hello, "+name);
            
        }
    }

      

      现在我想在 sayHello 方法中加一些逻辑怎么弄,但不可以改变原来的类代码,很容易想到的就是再写一个代理类,然后在代理类中调用原来的类。想好就赶快写:

    package com.hd.proxy;
    
    public class HelloProxy implements IHello {
    
        private IHello hello;
        public HelloProxy(IHello hello){
            this.hello = hello;
        }
        
        @Override
        public void sayHello(String name) {
            before();
            hello.sayHello(name);
            after();
            
        }
        
        private void before(){
            System.out.println("before...");
        }
        
        private void after(){
            System.out.println("after...");
        }
    }

      我创建了一个代理类 HelloProxy ,让它也实现了IHello接口,并定义了一个IHello的成员变量,这样我就可以在构造函数中作为参数给它赋值了。然后在代理类中的sayHello类里调用被代理类的方法。更重要的是,我还可以在调用的前后分别加上 before() 与 after() 方法,在这两个方法里去实现那些前后逻辑。

      用一个 main 方法来测试一下吧:

    package com.hd.proxy;
    
    public class TestProxy {
        public static void main(String[] args) {
            
            IHello hello = new HelloImpl();
            IHello helloProxy = new HelloProxy(hello);
            helloProxy.sayHello("jack");
        }
    }

      运行后结果为:

    before...
    hello, jack
    after...

      一个代理模式就这样被我写好了,有没有感觉 so easy。

      不过细心的同学,会发现这里有点小问题。就是如果只是代理一个类的话没有问题,但是你想想,如果我现在想要给 HelloImpl2,HelloImpl3 。。。等等这些类都要设置代理类怎么办,按照现在的做法,就是我们需要再定义 HelloProxy2,HelloProxy3.。。。等等同样多的代理类出来,这样会造成工程里的类数量激增,就是我们熟知的类爆炸。对于程序的维护很不好。

      因此下面我就介绍了动态代理,看看它是怎么实现的。

      动态代理分为两种,JDK动态代理和CGLIB动态代理。

      a) JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

      下面我用 JDK 给我们提供的动态代理方案,写了一个 DynamicProxy:

    package com.hd.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class DynamicProxy implements InvocationHandler {
    
        private Object target;
        public DynamicProxy(Object target) {
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
        
        private void before(){
            System.out.println("before...");
        }
        
        private void after(){
            System.out.println("after...");
        }
    }

      在 DynamicProxy 类中,定义了一个 Object 类型的 target 变量,它就是被代理的目标对象,通过构造函数来初始化。然后在invoke方法里通过反射调用被代理类(target)的方法(method.invoke(target, args))。

      下面写一个main方法,看看它是怎么实现动态代理的:

        public static void main(String[] args) {
            
            IHello hello = new HelloImpl();
            DynamicProxy handler = new DynamicProxy(hello);
            IHello h2 = (IHello)Proxy.newProxyInstance(
                    hello.getClass().getClassLoader(), 
                    hello.getClass().getInterfaces(), handler);
            h2.sayHello("Lee");
    
        }

      运行一下,结果和以前一样。 我写的这个 DynamicProxy 就是一个通用的代理逻辑类,不管什么类,只要它实现了某个接口,都可以 DynamicProxy 类来实现它定义的代理逻辑。是不是很方便。

      b)如果目标类没有实现接口,我们可以选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行期间动态生成字节码,运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

      说起来好高深,实际用起来一点都不难。下面再搞一个 CGLibProxy。

    package com.alimama.proxy;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            
            before();
            Object result = proxy.invokeSuper(obj, args);
            after();
            return result;
        }
        
        private void before(){
            System.out.println("before...");
        }
        
        private void after(){
            System.out.println("after...");
        }
    }

      我们只需要实现 CGLib 给我们提供的 MethodInterceptor 实现类,并填充 intercept() 方法就可以了。

      下面写个main方法,看看怎么使用这个代理类的:

    package com.hd.proxy;
    import net.sf.cglib.proxy.Enhancer;
    
    public class TestProxy {
    
        public static void main(String[] args) {
    
            CglibProxy cglibProxy = new CglibProxy();
            HelloImpl helloProxy = (HelloImpl)Enhancer.create(HelloImpl.class, cglibProxy);
            helloProxy.sayHello("bruce");
        }
    
    }

      我们只需要调用 Enhancer 的create方法,传入被代理类的class对象,以及我们创建的 CglibProxy 对象即可。

      总结一下,这篇我们谈到了 静态代理,指出了静态代理模式的弊端,进而介绍了JDK动态代理和Cglib 动态代理两种实现方式。它们俩兄弟在Spring AOP 中都运用到了。大家有时间可以去研究一下。

      感谢阅读,未完待续。。。

  • 相关阅读:
    [Qt] 文本文件读写, 摘自官方文档
    [Windows] Socket Server Failed to bind, error 10048
    lodctr /R 失败的情况
    ModuleNotFoundError: No module named 'sklearn.cross_validation'
    [Qt] 通过socket将另一个程序的某个窗口调到最前端
    SortedDictionary<TKey, TValue> 类 表示根据键进行排序的键/值对的集合。
    finally不管有没有错都会运行 finally 块用于清除 try 块中分配的任何资源,以及运行任何即使在发生异常时也必须执行的代码
    HttpWebRequest使用证书请求
    string StartsWith 方法 Https
    设置https验证方式
  • 原文地址:https://www.cnblogs.com/xiexin2015/p/9061105.html
Copyright © 2011-2022 走看看