java设计模式之代理模式

概述

最近在看Spring Aop,而Spring Aop的底层就是通过代理模式实现的。所以想要看懂Spring底层源码,设计模式是必须要掌握的!

什么是代理模式呢?
答:一个类代表另一个类的功能,就是代理模式。
其原理图如下:

原理图

上图中,ProxyImage类是代理类,代理的类是RealImage,两者都实现了Image接口。用户如果想要访问RealImage的方法,只需要访问代理类就可以了!

代理模式分为静态代理与动态代理,下面我们分别对这两种代理进行讲解。

静态代理

测试

我们以实际生活中的租房为例,我们平时租房,一般都是联系中介,中介再找房东租房,最后中介再把从房东哪里租来的房子再租给我们。

我们分析一下上述案例,需要如下几个对象:

  • 被代理对象: HouseOwner,房东
  • 代理类: Proxy,中介
  • 代理类与被代理类共同实现的接口: Rent
  • 客户端: Tenant,房客类

分析完之后,下面我们来编写我们的代码:

  1. 编写公共接口Rent

    package com.xdw;
    
    public interface Rent {
        void rent();
    }
    
  2. 编写被代理类HouseOwner实现我们刚刚创建的Rent接口

    package com.xdw;
    
    public class HouseOwner implements Rent {
        @Override
        public void rent() {
            System.out.println("房东出租房子!");
        }
    }
    
  3. 编写代理类,同样需要继承Rent接口

    package com.xdw;
    
    public class Proxy implements Rent {
    
        // 设置一个类型为Rent的属性,方便我们调用被代理类的方法
        private Rent rent;
    
        /**
         * 构造方法:将被代理类注入到代理类中
         * @param rent
         */
        public Proxy(Rent rent) {
            this.rent = rent;
        }
    
        @Override
        public void rent() {
            System.out.println("代理帮忙租房开始!");
            // 房东租房
            rent.rent();
            System.out.println("代理帮忙租房结束!");
        }
    }
    
  4. 编写房客类Tenant来调用代理类的Proxy方法

    package com.xdw;
    
    public class Tenant {
        public static void main(String[] args) {
            HouseOwner houseOwner = new HouseOwner();
            Proxy proxy = new Proxy(houseOwner);
            proxy.rent();
        }
    }
    
  5. 测试,运行
    静态代理运行结果

总结

看了上面的例子,可能有人会说,租房我直接找房东去租不就好了,中间加了一层代理,代码都比以前麻烦了,为什么要使用代理模式呢?

代理模式的好处:

- 可以是真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共任务就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候方便集中管理

缺点:

- 一个真实角色就会产生一个代理角色,代码量会翻倍

静态代理有个致命的缺点,一个真实角色就需要一个代理,会导致我们的代码量翻倍,那么有什么方法能解决这个问题呢?
答:使用动态代理!

动态代理

  • 动态代理与静态代理角色一样
  • 动态代理的类是自动生成的,不是直接写好的
  • 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
    • 基于接口: JDK的动态代理 【我们使用】
    • 基于类: cglib
    • java字节码实现:javassist

这里我们只用基于JDK的动态代理来进行测试!

测试

  1. 编写公共接口Rent

    package com.xdw;
    
    public interface Rent {
        void rent();
    }
    
  2. 编写被代理类HouseOwner实现我们刚刚创建的Rent接口

    package com.xdw;
    
    public class HouseOwner implements Rent {
        @Override
        public void rent() {
            System.out.println("房东出租房子!");
        }
    }
    
  3. 编写代理类调用处理程序类,该类需要实现InvocationHandler接口

    package com.xdw;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MyInvocationHandler implements InvocationHandler {
    
        private Object obj;
    
        public MyInvocationHandler(Object obj) {
            this.obj = obj;
        }
    
        /**
         * 动态获取代理类实例
         * @return
         */
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
        }
    
    
        /**
         * 利用反射执行代理类实例方法,可以在该方法中添加一些公共的逻辑,比如日志打印等
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("方法被调用之前,可以编写一些逻辑");
            Object result = method.invoke(obj,args);
            System.out.println("方法调用完毕,可以编写一些逻辑");
            return result;
        }
    }
    
  4. 编写房客类

    package com.xdw;
    
    public class Tenant {
        public static void main(String[] args) {
            HouseOwner houseOwner = new HouseOwner();
            MyInvocationHandler handler = new MyInvocationHandler(houseOwner);
            // 获取代理对象
            Rent proxy = (Rent)handler.getProxy();
            proxy.rent();
        }
    }
    
  5. 运行,测试
    动态代理测试结果

总结

  • 利用JDK实现动态代理需要使用到Proxy类与InvocationHandler接口
  • 每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
posted @   卧龙戏公瑾  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
点击右上角即可分享
微信分享提示