zoukankan      html  css  js  c++  java
  • 设计模式(四)——代理模式 Craftsman

    一、概述

    1、介绍

      代理模式(Proxy):为一个对象提供一个代理,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
      被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
      代理模式有不同的形式,主要有三种,静态代理、动态代理(JDK代理、接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,属于动态代理的范畴)。

    二、静态代理

    1、介绍

      静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同父类。调用的时候通过调用代理对象的方法来调用目标对象。

    2、模式原理

    3、代码

      代码示例:静态代理

     1 // 接口
     2 public interface ITeacher {
     3     void teach();
     4 }
     5 
     6 // 目标对象
     7 public class Teacher implements ITeacher {
     8     @Override
     9     public void teach() {
    10         System.out.println("老师正在上课");
    11     }
    12 }
    13 
    14 // 代理对象,静态代理
    15 public class TeacherProxy implements ITeacher {
    16     // 目标对象,通过构造器来聚合
    17     private Teacher teacher;
    18 
    19     public TeacherProxy(Teacher teacher) {
    20         this.teacher = teacher;
    21     }
    22 
    23     @Override
    24     public void teach() {
    25         System.out.println("开始静态代理...");
    26         // ...
    27         // 执行目标对象的方法
    28         teacher.teach();
    29         // ...
    30         System.out.println("静态代理提交...");
    31     }
    32 }
    33 
    34 public class Main {
    35     public static void main(String[] args) {
    36         // 创建代理对象,同时将目标对象作为参数传递
    37         TeacherProxy teacherProxy = new TeacherProxy(new Teacher());
    38         // 通过代理对象执行目标对象的方法
    39         teacherProxy.teach();
    40     }
    41 }

    4、优缺点

      优点:在不修改目标对象功能的前提下,能通过代理对象对目标功能进行扩展。
      缺点:因为代理对象需要与目标对象实现相同的接口,所以会有很多代理类。一旦接口增加方法,目标对象与代理对象都要维护。

    三、动态代理(JDK代理)

    1、介绍

      代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。代理对象的生成是利用JDK的API,动态的在内存中构建代理对象,动态代理也叫JDK代理、接口代理。

    2、模式原理

      根据传入的目标对象,利用返回机制,返回一个代理对象。然后通过代理对象,调用目标对象方法。

    3、代码

      代码示例:动态代理,JDK代理

     1 // 接口
     2 public interface ITeacher {
     3     void teach();
     4 
     5     int add(int i, int j);
     6 }
     7 
     8 // 目标对象
     9 public class Teacher implements ITeacher {
    10     @Override
    11     public void teach() {
    12         System.out.println("老师正在上课");
    13     }
    14 
    15     @Override
    16     public int add(int i, int j) {
    17         System.out.println("老师正在做加法");
    18         return i + j;
    19     }
    20 }
    21 
    22 // 代理对象,动态代理
    23 public class TeacherProxy {
    24     // 目标对象,通过接口来聚合
    25     private Object target;
    26 
    27     public TeacherProxy(Object target) {
    28         this.target = target;
    29     }
    30     
    31     // 给目标对象生成一个代理对象
    32     public Object getProxyInstance() {
    33         // ClassLoader loader:指定当前目标对象使用的类加载器
    34         // Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方式确认
    35         // InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器方法,会把当前执行的目标对象方法作为参数传入
    36         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    37                 target.getClass().getInterfaces(),
    38                 new InvocationHandler() {
    39                     @Override
    40                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    41                         System.out.println("JDK动态代理开始...");
    42                         // 通过反射机制调用目标对象的方法
    43                         Object invoke = method.invoke(target, args);
    44                         System.out.println("JDK动态代理提交...");
    45                         return invoke;
    46                     }
    47                 });
    48     }
    49 }
    50 
    51 public class Main {
    52     public static void main(String[] args) {
    53         // 创建代理对象,同时将创建的目标对象作为参数传递
    54         TeacherProxy proxy = new TeacherProxy(new Teacher());
    55 
    56         // 通过代理对象执行目标对象的方法
    57         ITeacher iTeacher = (ITeacher) proxy.getProxyInstance();
    58         iTeacher.teach();
    59 
    60         int add = iTeacher.add(1, 2);
    61         System.out.println(add);
    62     }
    63 }

    四、Cglib代理

    1、介绍

      目标对象不需要实现接口。静态代理和JDK代理都要求目标对象实现一个接口。但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,即Cglib代理。
      Cglib代理也叫作子类代理,它是内存中构建一个子类对象从而实现对目标对象功能扩展,Cglib代理也可以归属到动态代理。
      Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP框架使用,例如Spring AOP,实现方法拦截。
      在AOP编程中如何选择代理模式:①目标对象需要实现接口:用JDK代理。②目标对象不需要实现接口:用Cglib代理。
      Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

    2、代码

      代码示例:动态代理,cglib代理

     1 // 目标对象.未实现接口
     2 public class Teacher {
     3 
     4     public void teach() {
     5         System.out.println("老师正在上课");
     6     }
     7 
     8     public int add(int i, int j) {
     9         System.out.println("老师正在做加法");
    10         return i + j;
    11     }
    12 }
    13 
    14 // 代理对象
    15 public class ProxyFactory implements MethodInterceptor {
    16     private final Object target;
    17 
    18     public ProxyFactory(Object target) {
    19         this.target = target;
    20     }
    21 
    22     public Object getProxyInstance() {
    23         // 1.创建一个工具类
    24         Enhancer enhancer = new Enhancer();
    25         // 2.设置父类
    26         enhancer.setSuperclass(target.getClass());
    27         // 3.设置回调函数
    28         enhancer.setCallback(this);
    29         // 4.创建子类对象,即代理对象
    30         return enhancer.create();
    31     }
    32 
    33     @Override
    34     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    35         System.out.println("cglib代理模式开始...");
    36         Object o1 = method.invoke(target, objects);
    37         System.out.println("cglib代理模式结束...");
    38 
    39         return o1;
    40     }
    41 }
    42 
    43 public class Main {
    44     public static void main(String[] args) {
    45         // 创建目标对象
    46         Teacher teacher = new Teacher();
    47         
    48         // 生成目标对象的代理对象
    49         Teacher teacherProxy = (Teacher) new ProxyFactory(teacher).getProxyInstance();
    50 
    51         int add = teacherProxy.add(1, 2);
    52         System.out.println("----" + add);
    53     }
    54 }

      注意:cglib代理在内存中动态构建子类,代理的类不能为final,否则报错java.lang.IllegalArgumentException。
      目标对象的方法如果为final/static,就不会被代理,即不会执行目标对象额外的业务方法。

    3、应用

      性能开发:比如,监控、统计、鉴权、限流、事务、幂等、日志。将这些性能类功能与业务逻辑功能解耦,放到代理类中实现,让程序员更专注于业务方面的开发。典型例子就是 SpringAOP。
      RPC:远程代理框架也可以看作一种代理模式。通过远程代理,将网络通信、数据编解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解跟服务器交互的细节。除此之外,RPC 服务的开发者也只需要开发业务逻辑,就像开发本地使用的函数一样,不需要关注跟客户端的交互细节。
      防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
      缓存代理:比如,当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok;如果取不到资源,再到公网或者数据库取,然后缓存。
      同步代理:主要使用在多线程编程中,完成多线程间同步工作。

    作者:Craftsman-L

    本博客所有文章仅用于学习、研究和交流目的,版权归作者所有,欢迎非商业性质转载。

    如果本篇博客给您带来帮助,请作者喝杯咖啡吧!点击下面打赏,您的支持是我最大的动力!

  • 相关阅读:
    【剑指Offer】跳台阶&变态跳台阶
    【剑指Offer】替换空格
    【剑指Offer】二维数组中的查找
    【Leetcode】2. Add Two Numbers 两数相加
    HTML学习笔记(一)HTML的一些概念区别
    C#项目中一些文件类型说明
    数据结构初步归纳(一)概念、线性表以及队列和栈
    线程相关概念
    程序开发方法论
    C#集合类型
  • 原文地址:https://www.cnblogs.com/originator/p/15787568.html
Copyright © 2011-2022 走看看