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

    一、模式名

    代理, Proxy

    二、解决的问题

    “代理”这个词我们应该不陌生,在我们的生活中经常使用代理。例如:很多去国外旅游的人都会通过旅游中介公司购买机票、国外景点门票以及规划路线和寻找导游等,其实这就是一种代理模式,把自己不想直接做和不了解的事情交给专业的人去做,这样更加放心,效率也更高。

    在软件设计中,同样如此,很多优秀的开发框架都是会使用代理模式。在软件设计中的代理模式主要用于:

    1. 安全问题:通过使用代理,可以避免客户端直接访问真实的处理对象,可以控制对象的访问权限;

    2. 功能扩展:在很多框架中使用代理来扩展某些功能。比如在Spring中的AOP就利用了代理模式,通过在代理类中织入日志、安全等功能,实现对委托类对象功能的扩展。

    三、解决方案

    代理模式的UML图如下图所示:

    clipboard

    其中Subject为接口,定义需要的方法,代理类ProxySubject和代理类RealSubject都实现Subject接口,同时ProxySubject依赖RealSubject对象完成具体的操作。

    具体的代理主要分为两类:静态代理和动态代理。静态代理和动态代理的区别在于静态代理的代理类由程序员编写,代理对象是在通过编译该代理类生成,而动态代理的代理对象是在程序运行时通过反射机制生成。

    1. 静态代理

    静态代理就是手动编写代理类,代理类通过引用委托类对象完成具体操作。Java代码如下。

    interface Subject {
        void method1();
    }
    
    RealSubject implements Subject {
        void method1() {
            // 实现方法体
            ...
        }
    }
    
    ProxySubject implements Subject {
        private Subject subject;
        
        public ProxySubject(Subject subject) {
            this.subject = subject;
        }
        
        void method1() {
            // 执行前
            before();
            // 调用委托类对象完成具体操作
            subject.method1();
            // 执行后
            after();
        }
        
        void before() {}
        void after() {}
    }
    
    public class Main {
        public static void main(String[] args) {
            Subject subject = new ProxySubject(new RealSubject());
            subject.method1();
        }
    }

    可以看到静态代理需要针对每个委托类编写一个代理类,如果接口修改,代理类也需要做相应修改。

    2. 动态代理

    动态代理不需要程序员手动编写代理类,动态代理分为JDK代理和CGLib代理,前者针对其父类是接口的情况,后者针对父类是普通类的情况。

    JDK 代理 Java代码如下所示。

    public class DynamicProxyHandler implements InvocationHandler {
        private Object target;
        
        public DynamicProxyHandler() {
            this.obj = target;    
        }
        
        public Object invoke(Object obj, Method method, Object[] args) 
                                throws Throwable {
            // before
            before();
            Object result = method.invoke(target, args);
            // after
            after();
            return result;
        }
        
        void before() {}
        void after() {}
    }
    
    public class Main {
        public static void main(String[] args) {
            Subject subject = new RealSubject();
            Subject proxySubject = (Subject) Proxy.newProxyInstance(
                    Main.class.getClassLoader(), new Class[] {Subject.class}, 
                    new DynamicProxyHandler(subject));
            proxySubject.method1();
        }
    }

    其中,Proxy.newProxyInstance()方法有三个参数,桉顺序分别是类加载器,委托类实现的接口,代理类动态处理器。

    JDK代理的缺点就是只能针对interface代理,下面CGLib代理使用字节码技术实现针对class的代理。

    CGLib 代理 Java代码如下所示。

    class Subject {
        void method1() {}
    }
    
    RealSubject extends Subject {
        void method1() {
            // 方法体
            ...
        }
    }
    
    public class CglibProxy implements MethodInterceptor {
        private Object target;
        
        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperClass(this.target.getClass());
            enhancer.setCallBack(this);
            return enhancer.create();
        }
        
        public Object intercept(Object object, Method method, Object[] args, 
                        MethodProxy methodProxy) throws Throwable {
            // before
            before();
            Object object = methodProxy.invoke(target, args);
            // after
            after();
            
            return object;
        }
        
        void before();
        void after();
    }
    
    public class Main {
        public static void main(String[] args) {
            Subject subject = new RealSubject();
            CglibProxy proxy = new CglibProxy();
            Subject proxySubject = (Subject) 
                                        proxy.getInstance(subject);
            proxySubject.method1();
        }
    }

    常见应用场景:

    1. Spring中的 AOP

  • 相关阅读:
    javaSE基础知识点
    java自定义注解
    java自定义线程池
    java写投票脚本自动化初探
    java线程安全初窥探
    锁的深入理解
    java守护线程与非守护线程
    java设计模式初探
    java内存模型初窥探
    uniapp中组件之间跳转遇到的问题
  • 原文地址:https://www.cnblogs.com/glsy/p/11062220.html
Copyright © 2011-2022 走看看