zoukankan      html  css  js  c++  java
  • 通俗易懂讲解一下代理模式

    1.什么是代理

    In computer programming, the proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.

    (所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。)

    ​ —— 引用自维基百科

    维基百科所说的优点抽象,这里我们简单来讲就是通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。

    2.代理的应用场景和分类

    ①代理应用场景

    • 安全代理 可以屏蔽真实角色
    • 远程代理 远程调用代理类RMI
    • 延迟加载 先加载轻量级代理类,真正需要在加载真实

    ②代理的分类

    • 静态代理(静态定义代理类)
    • 动态代理(动态生成代理类)
    • Jdk自带动态代理
    • Cglib 、javaassist(字节码操作库)

    3.具体实现步骤

    业务场景描述:

    ​ 某买家去看房,觉得采光和低端都不错,决定买下这个房子。这个时候买家就找到中介公司,也就是我们的代理者,帮我们的买家处理售前售后工作,并安排好一切交付事项。这里的一切都围绕房子所展开,于是我们可以把房源(或者房地产商)当做是我们的接口,然后我们的中介机构代理相关事项,买家谈妥后并不需要和房地产商有什么直接的交涉便可一步到位。

    ①静态代理

    ​ 这里我们根据房源,分别让顾客和中介实现相关接口方法,把买家交给中介结构处理相关事项,然后调用相关方法即可。

    /**
     * 真实房源
     */
    interface House {
        void maifang();
    }
    
    /**
     * 顾客
     */
    class Customer implements House {
    
        @Override
        public void maifang() {
            System.out.println("我是买家,终于可以买房了");
        }
    }
    
    /**
     * 中介
     */
    class Proxy implements House {
    
        // 代理对象
        private Customer customer;
    
        public Proxy(Customer customer) {
            this.customer = customer;
        }
    
        @Override
        public void maifang() {
            System.out.println("我是中介,你买房开始交给我啦!");
            customer.maifang();
            System.out.println("我是中介,你买房结束啦...");
        }
    }
    
    // 静态代理
    public class Simulation {
    
        public static void main(String[] args) {
    
            Customer customer = new Customer();
            House house = new Proxy(customer);
            house.maifang();
        }
    }
    

    输出结果如下:

    我是中介,你买房开始交给我啦!
    我是买家,终于可以买房了
    我是中介,你买房结束啦...
    
    ②JDK动态代理

    ​ 动态代理需要实现InvocationHandler接口,相比静态代理而言有一个好处就是:不需要生成代理类,可扩展性强,方便后续的更改和操作。

    /**
     * 真实房源
     */
    interface House {
        void maifang();
    }
    
    /**
     * 顾客
     */
    class Customer implements House {
    
        @Override
        public void maifang() {
            System.out.println("我是买家,终于可以买房了");
        }
    
        public void sayHello() {
            System.out.println("sayHello");
        }
    }
    
    /**
     * 中介
     * 动态代理
     */
    
    class JdkProxy implements InvocationHandler {
    
        private Object target;
    
        public JdkProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
            System.out.println("动态代理:我是中介,你买房开始交给我啦!");
            Object invoke = method.invoke(target, args);
            System.out.println("我是中介,你买房结束啦...");
            return invoke;
        }
    }
    
    
    // 动态代理
    public class Simulation {
    
        public static void main(String[] args) {
    
            Customer customer = new Customer();
            JdkProxy jdkProxy = new JdkProxy(customer);
            House house = (House)Proxy.newProxyInstance(customer.getClass().getClassLoader(), 							customer.getClass().getInterfaces(),jdkProxy);
            house.maifang();
        }
    }
    

    输出结果如下:

    动态代理:我是中介,你买房开始交给我啦!
    我是买家,终于可以买房了
    我是中介,你买房结束啦...
    
    ③CGLIB代理

    ​ CGLIB代理的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    /**
     * 真实房源
     */
    interface House {
        void maifang();
    }
    
    /**
     * 顾客
     */
    class Customer implements House {
    
        @Override
        public void maifang() {
            System.out.println("我是买家,终于可以买房了");
        }
    
        public void sayHello() {
            System.out.println("sayHello");
        }
    }
    
    /**
     * Cglib代理
     */
    class CglibProxy implements MethodInterceptor {
    
        private Object target;
    
        public CglibProxy(Object target) {
            this.target = target;
        }
    
        public Object create() {
            Enhancer enhancer = new Enhancer();
            enhancer.setClassLoader(target.getClass().getClassLoader());
            enhancer.setInterfaces(target.getClass().getInterfaces());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy 									methodProxy) throws Throwable {
            System.out.println("我是中介,你买房开始交给我啦!");
            Object invokeSuper = methodProxy.invoke(target, args);
            System.out.println("我是中介,你买房结束啦...");
            return invokeSuper;
        }
    }
    
    public class Simulation {
        public static void main(String[] args) {
            Customer customer = new Customer();
            CglibProxy cglibProxy = new CglibProxy(customer);
            House o = (House) cglibProxy.create();
            o.maifang();
        }
    }
    

    ​ 输出结果和上面的效果一样,这里不做重复展示,值得一提的CGLIB代理是使用Enhancer来处理相关的类和接口的实现的,这个地方是其独有的特点。

    4.CGLIB代理与JDK动态代理的区别

    ​ java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
    ①如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
    ②如果目标对象实现了接口,可以强制使用CGLIB实现AOP
    ③如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

  • 相关阅读:
    免备案 国外服务器 vps 推荐以及优惠码
    VSCode 实时预览 HTML 页面
    VPS 上安装 Nginx 就是这么简单
    Thinkphp 5 验证码无法正常显示的排错以及解决方案 【已解决】
    Composer 安装 topthink/think-captcha 时报错 requires topthink/framework ^6.0.0 【已解决】
    Linux ps命令
    MySQL 查询不区分大小写的问题以及编码格式问题
    Cyberduck 小黄鸭 跨平台的 FTP 软件
    MySQL 存储过程 详解
    php 四舍五入保留两位小数,自动补齐0
  • 原文地址:https://www.cnblogs.com/charlypage/p/11747586.html
Copyright © 2011-2022 走看看