笔头云 笔头云
首页
设计模式
SQL教程
Redis
归档
关于
友链

笔头云

非淡泊无以明志,非宁静无以致远。
首页
设计模式
SQL教程
Redis
归档
关于
友链
  • 设计原则
  • 设计模式
  • 单例模式
  • 工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 代理模式
    • 定义
    • 结构
    • 优点
    • 缺点
    • 应用场景
    • 示例代码1
  • 装饰器模式
  • 门面模式
  • 桥接模式
  • 享元模式
  • 组合模式
  • 策略模式
  • 模板方法模式
  • 观察者模式
  • 责任链模式
  • 状态模式
  • 迭代器模式
  • 访问者模式
  • 中介模式
  • 命令模式
  • 解释器模式
  • 备忘录模式
  • 设计模式
笔头云
2023-10-31
目录

代理模式

# 代理模式

代理模式,通过引入一个代理对象来间接访问原始对象,以控制对原始对象的访问。代理对象充当了客户端和原始对象之间的中介,可以在访问原始对象之前或之后执行一些额外的逻辑操作。

# 定义

代理模式,为其他对象提供一种代理(Proxy)以控制对这个对象的访问。代理是指具有与被代理的对象具有相同的接口的类,客户端通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。
代理模式又分为静态代理和动态代理。动态代理常用 JDK代理和 CGLIB代理。
静态代理: 在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
动态代理:在编译期间无法确定需要代理的类。运行期间动态生成代理类。

JDK动态代理与CGLIB动态代理区别

使用jdk代理的目标类必须实现至少一个接口,因为它是通过java反射实现,基于接口创建代理对象。使用cglib代理的目标类则无需实现,但其不能被final修饰,因为其运用了继承关系。

# 结构

抽象主题(Subject):定义了真实主题(Real Subject)和代理(Proxy)之间的共同接口,客户端通过该接口访问真实主题或代理对象。
真实主题(Real Subject):定义了代理所代表的真实对象,是被代理的具体业务对象。真实主题包含了真正的业务逻辑,代理通过调用真实主题的方法来完成具体的业务。
代理(Proxy):实现了抽象主题接口,同时持有对真实主题的引用。代理对象在客户端和真实主题之间充当了中介的角色,它可以在调用真实主题之前或之后执行一些额外的逻辑操作。

代理模式

- img: https://bitouyun.com/images/design-pattern/proxy.png
  link: https://bitouyun.com/images/design-pattern/proxy.png
  name: 代理模式
1
2
3

# 优点

隐藏对象的具体实现:代理模式可以隐藏原始对象的具体实现细节,使客户端只关注代理对象的接口。这样可以提高系统的安全性和稳定性,防止客户端直接访问和修改原始对象。
访问控制:代理模式可以在代理对象中添加访问控制的逻辑,限制对原始对象的访问。例如,可以在代理对象中检查客户端的权限,只有满足一定条件的客户端才能访问原始对象。
延迟加载:代理对象可以在真正需要时才创建和初始化原始对象,可以避免在一开始就创建代价昂贵的原始对象。
缓存数据:代理对象可以在访问原始对象之前或之后缓存数据,以提高系统的响应速度。例如,代理对象可以缓存一些频繁访问的数据,当下次请求时,可以直接返回缓存的数据,而无需再次访问原始对象。

# 缺点

增加复杂性:引入代理模式会增加系统的复杂性,需要定义和管理额外的代理类。如果系统中有多个原始对象,就需要为每个原始对象定义相应的代理类,增加了代码量和维护成本。
增加间接性:代理模式引入了额外的代理层,导致客户端访问原始对象变得间接,增加了访问的复杂性和开销。
可能影响系统性能:在某些情况下,代理模式可能会对系统性能产生一定的影响。例如,在频繁访问原始对象的情况。

# 应用场景

代理模式在需要控制对原始对象的访问、隐藏实现细节、提高性能或添加额外逻辑时是一种有用的设计模式。但在使用时需要权衡其带来的复杂性和性能影响。
代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在RPC、缓存等应用场景中。

代理模式与装饰器模式的区别

装饰器模式是动态地给对象添加额外的功能,通过包装器对象来包装原始对象,以扩展其功能。
装饰器模式注重对对象功能的增强和扩展,而代理模式注重对对象的控制访问。
装饰器模式通过包装器对象来包装原始对象,形成一条装饰链;代理模式通过代理对象来控制对原始对象的访问。
装饰器模式保持了原始对象的接口完整性,客户端可以透明地使用装饰后的对象;代理模式可以隐藏原始对象的细节,客户端可能无法直接访问原始对象。
实际上,装饰器模式可以看作是一种特殊的代理模式,其中代理对象的主要职责是增强原始对象的功能。因此,装饰器模式和代理模式可以相互补充,并在某些情况下可以互换使用。

# 示例代码1

场景1

添加用户业务,使用代理模式记录消耗时间日志。

    /**
     * 抽象主题
     * 定义了真实主题(Real Subject)和代理(Proxy)之间的共同接口,
     * 在任何需要真实主题的地方都可以使用代理。
     */
    public interface UserService {
    
      /**
       * 添加用户
       * @param username 用户名
       */
      void addUser(String username);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
     * 真实主题
     * 实现抽象主题,实际业务实现
     */
    @Slf4j
    public class UserServiceImpl implements UserService{
      @Override
      public void addUser(String username) {
        // 添加用户具体业务实现
        log.info("添加用户:{}", username);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
     * 静态代理
     * 实现UserService抽象主题接口,并持有一个真实主题的引用。
     */
    @Slf4j
    public class UserServiceProxy implements UserService {
      private UserService realSubject;
    
      public UserServiceProxy(UserService realSubject) {
        this.realSubject = realSubject;
      }
    
      @Override
      public void addUser(String username) {
        long startTime = System.currentTimeMillis();
        // 调用真实主题,添加用户业务
        realSubject.addUser(username);
        long endTime = System.currentTimeMillis();
        log.info("静态代理添加用户,耗时:{}", endTime - startTime);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * JDK动态代理
     */
    @Slf4j
    public class UserServiceJDKProxy implements InvocationHandler {
    
      private Object realSubject;
    
      public UserServiceJDKProxy(Object realSubject) {
        this.realSubject = realSubject;
      }
    
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        // 调用添加用户业务
        Object result = method.invoke(realSubject, args);
        long endTime = System.currentTimeMillis();
        log.info("jdk动态代理添加用户,耗时:{}", endTime - startTime);
        return result;
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * CGLIB动态代理
     */
    @Slf4j
    public class UserServiceCglibProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object result = proxy.invokeSuper(obj, args);
            long endTime = System.currentTimeMillis();
            log.info("CGLIB动态代理添加用户,耗时:{}", endTime - startTime);
            return result;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import net.sf.cglib.proxy.Enhancer;
    import java.lang.reflect.Proxy;
    
    /**
     * 测试类
     */
    public class Client {
        public static void main(String[] args) {
            // 静态代理
            UserService realSubject = new UserServiceImpl(); // 真实主题
            UserService proxy = new UserServiceProxy(realSubject); // 代理
            proxy.addUser("Mike");
    
            // JDK动态代理
            UserService jdkProxy = (UserService) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                    realSubject.getClass().getInterfaces(),
                    new UserServiceJDKProxy(realSubject));
            jdkProxy.addUser("Alice");
    
            // CGLIB动态代理(引入cglib库)
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(new UserServiceCglibProxy());
            UserServiceImpl cglibProxy = (UserServiceImpl) enhancer.create();
            cglibProxy.addUser("Bob");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // Make sure to add code blocks to your code group

    常见问题

    1. 添加CGLIB依赖
    <!-- CGLIB -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
    1
    2
    3
    4
    5
    6

    2.非法反射访问操作
    WARNING: An illegal reflective access operation has occurred
    IDEA设置VM OPTION:

    --add-opens java.base/java.lang=ALL-UNNAMED
    
    1

    img

    #结构型模式#设计模式
    上次更新: 2023/11/17, 09:38:49
    适配器模式
    装饰器模式

    ← 适配器模式 装饰器模式→

    最近更新
    01
    FRP内网穿透docker部署 工具
    05-07
    02
    Office Util办公工具 工具
    01-14
    03
    Git常用命令
    01-16
    更多文章>
    Theme by Vdoing | Copyright © 2023-2025 鲁ICP备2023014898号 公安备案号:37020302372159
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×