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

welcome to my blog

代理模式的定义

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

代理模式在生活中的体现

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

代理模式的分类

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

静态代理

三个关键:

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

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

  1. 创建目标对象
  2. 创建静态代理对象
  3. 使用静态代理对象调用目标对象的方法
//Subject是目标类和代理类的接口
interface Subject{
    void doSomething();
}

//RealSubject表示目标类
class RealSubject implements Subject{
    @Override
    public void doSomething() {
        System.out.println("向租客展示房子...");
    }
}

//Proxy表示代理类
class Proxy implements Subject{
    //代理类中需要有目标类的引用, 这样才能通过代理对象调用目标对象的方法
    //这里使用接口类型, 体现了"依赖倒转"原则, 也就是面向接口编程;

    Subject target;
    Proxy(Subject target){
        this.target = target;
    }
    @Override
    public void doSomething() {
        //在调用目标对象方法之前可以执行一些其他操作
        System.out.println("调用目标对象方法之前可以执行一些其他操作");
        //调用目标对象的方法
        target.doSomething();
        //在调用目标对象方法之后可以执行一些其他操作
        System.out.println("调用目标对象方法之后可以执行一些其他操作");
    }
}
public class StaticProxyDemo {
    public static void main(String[] args) {
        //使用代理模式, 如下三步
        //1.创建目标对象
        Subject subject = new RealSubject();
        //2.创建代理对象, 并将目标对象传给代理对象
        //创建Proxy对象时, 向构造函数中传入的是RealSubject实例, 并不是Subject,
        //体现了"里式替换"原则, 也就是把父类替换成子类, 程序的行为没有变化
        Subject proxy = new Proxy(subject);
        //3.通过代理对象调用目标对象的方法
        proxy.doSomething();
    }
}

静态代理优缺点

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

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

动态代理

动态代理不需要程序执行前就存在代理类的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{
    void doSomething();
}

//RealSubject表示目标类
class RealSubject implements Subject{
    @Override
    public void doSomething() {
        System.out.println("向租客展示房子...");
    }
}

//DynamicSubject表示动态代理类; 可以发现并不需要实现Subject接口
//之后会使用代理对象调用目标对象的方法
class DynamicSubject implements InvocationHandler {
    //代理类中需要有目标类的引用, 这样才能通过代理对象调用目标对象的方法
    Subject subject;
    DynamicSubject(Subject subject){
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在调用目标对象方法之前可以执行一些其他操作
        System.out.println("调用目标对象方法之前可以执行一些其他操作");
        //调用目标对象的方法; subject是目标对象; args是目标对象的method方法的参数
        method.invoke(subject, args); //不需要具体指明调用目标对象的哪个方法
        //在调用目标对象方法之后可以执行一些其他操作
        System.out.println("调用目标对象方法之后可以执行一些其他操作");
        return null;
    }
}

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

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

CGLib动态代理

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

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值