设计模式之代理模式 静态代理和动态代理

£神魔★判官ぃ 2023-07-26 05:27 92阅读 0赞

welcome to my blog

代理模式的定义

《大话设计模式》中对于代理模式的描述: 代理模式为其他对象提供一种代理以控制对这个对象的访问.
通俗点说就是, 通过一个中间对象访问目标对象.

代理模式在生活中的体现

租房时需要看房, 往往是中介的人带着咱们去看房, 并不是直接由房东带着去看房. 注意到房子是属于房东的属性, 但是我们并没有通过房东访问这个属性, 而是通过中介访问这个属性, 这种看房子的方式就体现了代理模式的思想

代理模式的分类

根据代理类出现的时机, 可以将代理模式分成静态代理和动态代理
静态代理: 程序执行前代理类便已经存在,也就是说程序执行前便存在代理类对应的class文件
动态代理: 程序执行中使用反射机制生成代理类

静态代理

三个关键:

  1. 目标类和代理类的抽象接口
  2. 目标类
  3. 静态代理类

使用静态代理的三个步骤:

  1. 创建目标对象
  2. 创建静态代理对象
  3. 使用静态代理对象调用目标对象的方法

    //Subject是目标类和代理类的接口
    interface Subject{

    1. void doSomething();

    }

    //RealSubject表示目标类
    class RealSubject implements Subject{

    1. @Override
    2. public void doSomething() {
    3. System.out.println("向租客展示房子...");
    4. }

    }

    //Proxy表示代理类
    class Proxy implements Subject{

    1. //代理类中需要有目标类的引用, 这样才能通过代理对象调用目标对象的方法
    2. //这里使用接口类型, 体现了"依赖倒转"原则, 也就是面向接口编程;
    3. Subject target;
    4. Proxy(Subject target){
    5. this.target = target;
    6. }
    7. @Override
    8. public void doSomething() {
    9. //在调用目标对象方法之前可以执行一些其他操作
    10. System.out.println("调用目标对象方法之前可以执行一些其他操作");
    11. //调用目标对象的方法
    12. target.doSomething();
    13. //在调用目标对象方法之后可以执行一些其他操作
    14. System.out.println("调用目标对象方法之后可以执行一些其他操作");
    15. }

    }
    public class StaticProxyDemo {

    1. public static void main(String[] args) {
    2. //使用代理模式, 如下三步
    3. //1.创建目标对象
    4. Subject subject = new RealSubject();
    5. //2.创建代理对象, 并将目标对象传给代理对象
    6. //创建Proxy对象时, 向构造函数中传入的是RealSubject实例, 并不是Subject,
    7. //体现了"里式替换"原则, 也就是把父类替换成子类, 程序的行为没有变化
    8. Subject proxy = new Proxy(subject);
    9. //3.通过代理对象调用目标对象的方法
    10. proxy.doSomething();
    11. }

    }

静态代理优缺点

优点:
实现了客户端和目标对象之间解耦和

缺点:
代理类需要实现接口中的所有方法, 如果接口中的方法改变了, 那么既需要修改目标类, 也需要修改代理类

动态代理

动态代理不需要程序执行前就存在代理类的class文件, 而是在程序执行中直接生成代理对象, 并且不需要实现接口中的所有方法.
动态代理有两种实现方式:
JDK动态代理:
Java提供的动态代理技术, 可以在运行时创建接口的代理实例
目标类必须存在接口

CGLib动态代理:
采用底层的字节码技术, 在运行时创建子类代理实例
目标类存在接口或者不存在接口都可以

JDK动态代理

三个关键:

  1. 目标类的抽象接口
  2. 目标类
  3. 动态代理类

使用JDK动态代理的三个步骤:

  1. 创建目标对象
  2. 创建动态代理对象
  3. 使用动态代理对象调用目标对象的方法

两个核心:

  1. 定义动态代理类时需要实现InvocationHandler接口
  2. 创建动态代理对象时需要使用Proxy类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    //Subject是目标类和代理类的接口
    interface Subject{

    1. void doSomething();

    }

    //RealSubject表示目标类
    class RealSubject implements Subject{

    1. @Override
    2. public void doSomething() {
    3. System.out.println("向租客展示房子...");
    4. }

    }

    //DynamicSubject表示动态代理类; 可以发现并不需要实现Subject接口
    //之后会使用代理对象调用目标对象的方法
    class DynamicSubject implements InvocationHandler {

    1. //代理类中需要有目标类的引用, 这样才能通过代理对象调用目标对象的方法
    2. Subject subject;
    3. DynamicSubject(Subject subject){
    4. this.subject = subject;
    5. }
    6. @Override
    7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    8. //在调用目标对象方法之前可以执行一些其他操作
    9. System.out.println("调用目标对象方法之前可以执行一些其他操作");
    10. //调用目标对象的方法; subject是目标对象; args是目标对象的method方法的参数
    11. method.invoke(subject, args); //不需要具体指明调用目标对象的哪个方法
    12. //在调用目标对象方法之后可以执行一些其他操作
    13. System.out.println("调用目标对象方法之后可以执行一些其他操作");
    14. return null;
    15. }

    }

    public class JDKProxyDemo {

    1. public static void main(String[] args) {
    2. //使用代理模式, 如下三步
    3. //1.创建目标对象
    4. Subject subject = new RealSubject();
    5. //2.创建动态代理对象proxy, 但是不能通过这个对象调用目标对象的方法,
    6. InvocationHandler proxy = new DynamicSubject(subject);
    7. //使用Proxy类将动态代理对象和目标对象联系起来, 生成真正的动态代理对象
    8. //newProxyInstance()需要传入三个参数:加载目标类的类加载器,目标类实现的接口, 动态代理对象proxy
    9. //newProxyInstance()返回的是Object类型, 需要强转成Subject类型
    10. Subject dynamicProxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), proxy);
    11. //3.使用Proxy类创建动态代理类
    12. dynamicProxy.doSomething();
    13. }

    }

JDK动态代理的特点

动态代理类的invoke方法中, 不需要具体指明调用目标对象的哪个方法
目标类必须存在接口, 对应到上面的例子就是RealSubject类实现了Subject的接口, 没有接口就不能使用JDK动态代理

CGLib动态代理

CGLib可以为没有实现接口的目标类创建代理
为目标类动态生成一个子类, 在子类中重写目标类的所有非final方法(因为final方法不能被子类继承). 最重要的是在子类中采用方法拦截技术, 拦截所有对父类方法的调用, 顺势织入横切逻辑

发表评论

表情:
评论列表 (有 0 条评论,92人围观)

还没有评论,来说两句吧...

相关阅读