Spring基础学习之代理模式

前言

小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个Spring基础学习系列是用来记录我学习Spring框架基础知识的全过程 (这个系列是参照B站狂神的Spring5最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习Spring5框架的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
废话不多说,让我们开始今天的学习内容吧,今天我们来到了Spring基础学习的第十站:代理模式

10.代理模式

为什么要学习代理模式?

因为这是Spring AOP的底层实现原理,因此我们要有所了解

代理模式又分为几种?

  • 静态代理
  • 动态代理

10.1 静态代理

10.1.1 角色分析

在这里插入图片描述

  • 抽象角色:一般会使用接口或者抽象类来实现 (例如将租房业务抽象成一个接口)
  • 真实角色:被代理的角色 (例如租房的房东)
  • 代理角色:代理真实角色 (例如帮助房东租房的中介),代理真实角色后,一般会做一些附属操作 (例如带客户看房,签合同,收中介费等)
  • 客户端:访问代理对象的人 (来租房的客户)

10.1.2 用静态代理实现租房案例

1.编写租房的接口
// 定义(抽象角色)租房接口
public interface Rent {
    
    // 租房rent()方法
    public void rent();
}
2.编写真实角色房东
// 实现租房接口的房东实体类(真实角色)
public class Host implements Rent {
    
    // 重写租房rent()方法
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}
3.编写代理角色中介
// 创建实现租房接口的代理角色(中介)
public class Proxy implements Rent {
    
    private Host host;
    
    // 无参构造器
    public Proxy() {

    }
    
    // 有参构造方法
    public Proxy(Host host) {
        this.host = host;
    }
    
    // 重写租房rent()方法
    public void rent() {
        host.rent(); // 租房子
        seeHouse(); // 看房子
        contract(); // 签合同
        fare(); // 收中介费
    }
    
    // 看出租房
    public void seeHouse() {
        System.out.println("中介带你看房子");
    }
    
    // 签合同
    public void contract() {
        System.out.println("签订租赁合同");
    }
    
    // 收中介费
    public void fare() {
        System.out.println("向房东收中介费");
    }
    
}
4.客户端访问代理角色
// 客户实体类
public class Client {
    public static void main(String[] args) {
        
        // 房东要租房子
        Host host = new Host();
        // 进行代理:中介帮房东租房子,但是代理角色一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        // 客户不用面对房东,直接找中介租房即可
        proxy.rent();
    }
    
}
5.测试结果

在这里插入图片描述

10.1.3 静态代理的优缺点

1.好处
  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的事务 (例如真实角色房东只需租房就可以了)
  • 公共事务就交给代理角色处理,实现了业务的分工,使耦合性降低 (看房和签合同等公共事务交由代理角色中介进行处理)
  • 公共业务发进行扩展时,方便集中管理 (例如房东有新的业务要拓展,比如收缴水电费,也可以交由代理角色中介处理,无需添加自身业务数量)
2.缺点
  • 一个真实角色就意味着会产生一个代理角色,因此代码量会翻倍,开发效率也会变低

10.1.4 静态代理的加深理解

1.编写抽象角色
// 创建用户服务接口(抽象角色)
public interface UserService {
    
    // 定义增删改查方法
    
    public void add();
    
    public void delete();
    
    public void update();
    
    public void query();
    
}
2.编写真实角色
// 用户服务接口的实现类(真实角色)
public class UserServiceImpl implements UserService {
    
    // OOP有七大原则,这里符合开闭原则:即软件实体对拓展开放,对修改关闭
    
    public void add() {
        System.out.println("增加一个用户");
    }
    
    public void delete() {
        System.out.println("删除一个用户");
    }
    
    public void update() {
        System.out.println("修改加一个用户");
    }
    
    public void query() {
        System.out.println("查询一个用户");
    }
    
    // 改动原有的业务代码,在公司中是大忌
    
}
3.编写代理角色
// 实现用户服务接口的代理角色
public class UserServiceProxy implements UserService {
    
    // 引入真实角色
    private UserServiceImpl userService;
    
    // 设置用户服务方法
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    
    /**
     * 重写用户服务接口的增删改查方法
     */
    public void add() {
        // 调用日志log()方法
        log("add");
        userService.add();
    }
    
    public void delete() {
        log("delete");
        userService.delete();
    }
    
    public void update() {
        log("update");
        userService.update();
    }
    
    public void query() {
        log("query");
        userService.query();
    }
    
    //日志方法
    public void log(String msg) {
        System.out.println("使用了"+msg+"方法");
    }
}
4.模拟客户端访问代理角色
public class Client {
    public static void main(String[] args) {
        
        // 实例化真实角色
       UserServiceImpl userService = new UserServiceImpl();
        // 实例化代理角色
       UserServiceProxy proxy = new UserServiceProxy();
       // 代理角色代理业务,但同时也会有其他附属操作
       proxy.setUserService(userService);
        // 客户端直接访问代理角色,无需面对真实角色
        proxy.query();
    }
    
}
5.测试结果

在这里插入图片描述

6.聊聊AOP

在这里插入图片描述

10.2 动态代理

10.2.1 动态代理初步了解

1.动态代理特点
  • 动态代理和静态代理的角色一样 (即真实角色,抽象角色,代理角色和客户端)

  • 动态代理的代理类是动态生成的,不是提前直接写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    ​ 1.基于接口:JDK动态代理

​ 2.基于类:cglib

​ 3. java字节码实现:javassist

2.基于接口的动态代理

在实现JDK动态代理之前,我们需要来了解两个类

一个是Proxy (代理),另一个是InvocationHandler (调用处理程序)

  • InvocationHandler:是由代理对象实例用来调用处理程序所实现的一个接口
  • Proxy:它是一个提供创建动态代理类和实例的静态方法

10.2.2 动态代理实现租房案例

1.编写抽象角色
// 抽象角色:租房接口
public interface Rent {
    
    // 租房rent()方法
    public void rent();
    
}
2.编写真实角色
// 真实角色房东:其实现了租房接口
public class Host implements Rent {
    
    // 重写租房rent()方法
    public void rent() {
        System.out.println("房东要出租房子!");
    }
    
}
3.编写动态代理工具类
// 该类能够自动生成代理类,相当于中介所 (因为它并不能直接进行代理,而是负责提供中介)
public class ProxyInvocationHandler implements InvocationHandler {
    
    // 被代理的租房接口类
    private  Rent rent;
    
    // 使用set方法实现动态实现值的注入
    public void setRent(Rent rent) {
        this.rent = rent;
    }
    
    // 生成得到代理类
    public Object getProxy() {
        // 返回值为调用代理类的newProxyInstance方法获取代理角色实例
        // 其中有三个参数:第一个参数是当前类的类加载器,第二个参数是创建实例的实现类的接口
        // 第三个参数是InvocationHandler调用处理程序接口
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }
    
    // 处理代理实例,进行增强,需要返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态代理的本质,就是使用反射机制实现
        // 前增强:看房子
        seeHouse();
        // 目标类的方式
        Object result = method.invoke(rent,args);
        // 后增强:收中介费
        fare();
        // 返回结果对象
        return result;
    }
    
    // 前增强方法:看房子
    public void seeHouse() {
        System.out.println("中介带客户看房子");
    }
    
    // 后增强方法:收中介费
    public void fare() {
        System.out.println("收中介费");
    }
    
}
4.模拟客户端访问代理角色
// 客户端访问代理角色
public class Client {
    public static void main(String[] args) {
        
        // 获取真实角色房东
        Host host = new Host();
        // 获取动态代理工具类(中介所):目前代理角色中介还没有出现
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        // 设置代理对象:通过调用动态代理工具类(中介所)的setRent方法来实现代理对象host的注入
        pih.setRent(host);
        // 动态生成代理角色类proxy(中介):代理角色中介这时才出现
        Rent proxy = (Rent) pih.getProxy();
        // 当调用代理类的rent方法时,会通过反射机制调用invoke方法来实现方法增强
        // 客户端直接访问中介来进行租房
        proxy.rent();
        
    }
}
5.测试结果

在这里插入图片描述

10.2.3 动态代理的好处

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的事务 (真实角色房东只需关注租房业务即可)
  • 公共事务就交给代理角色,实现了业务的分工,使耦合性降低 (看房子,签合同等公共事务,交由代理角色中介进行处理)
  • 公共业务发生扩展的时候,方便集中管理 (如果房东要扩展新业务,比如收缴水电费,交给代理角色中介就可以了,无需添加自身业务)
  • 一个动态代理类代理的就是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

10.2.4 动态代理 (单个真实对象)

1.编写抽象角色
// 创建用户服务接口(抽象角色)
public interface UserService {
    
    // 定义增删改查方法
    public void add();
    
    public void delete();
    
    public void update();
    
    public void query();
    
}
2.编写真实角色
// 用户服务接口的实现类(真实角色)
public class UserServiceImpl implements UserService {
    
    // 重写增删改查的方法
    public void add() {
        System.out.println("增加一个用户");
    }
    
    public void delete() {
        System.out.println("删除一个用户");
    }
    
    public void update() {
        System.out.println("修改加一个用户");
    }
    
    public void query() {
        System.out.println("查询一个用户");
    }
    
}
3.动态代理工具类
// ProxyInvocationHandler作用是生成动态代理实例,相当于中介所
// InvocationHandler作用是调用处理程序并返回结果
public class ProxyInvocationHandler implements InvocationHandler {
    
    //被代理的接口类
    private  Object target;
    
    // 使用set方法实现动态值的注入
    public void setTarget(Object target) {
        this.target = target;
    }
    
    // 生成得到代理类
    public Object getProxy() {
        // 返回值为代理对象实例:由代理对象Proxy调用newProxyInstance方法获得
        // 有三个参数:第一个是当前类的类加载器,第二个是创建实例的实现类的接口
        // 第三个是InvocationHandler调用处理程序接口
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    
    // 处理代理实例,进行方法增强,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态代理的本质,就是使用反射机制实现
        // 前增强:log日志方法
        log(method.getName());
        // 目标类的方式
        Object result = method.invoke(target,args);
        // 返回结果对象
        return result;
    }
    
    // 日志方法
    public void log(String msg) {
        System.out.println("执行了"+msg+"方法");
    }
    
}
4.模拟客户端访问代理对象
// 客户端角色:租房的人
public class Client {
    public static void main(String[] args) {
        
        // 获取真实角色
        UserServiceImpl userService = new UserServiceImpl();
        // 获取动态代理工具类(中介所):目前代理角色(中介)还未生成
        ProxyInvocationHandler pih= new ProxyInvocationHandler();
        // 设置要代理的对象
        pih.setTarget(userService);
        // 动态生成代理类实例:代理角色中介这时才出现
        UserService proxy = (UserService) pih.getProxy();
        // 调用代理类的方法时,通过反射机制自动调用invoke方法来实现方法增强
        // 代理类实例执行业务
        proxy.add();
        
    }
}
5.测试结果

在这里插入图片描述

10.2.5 动态代理 (多个真实对象)

1.编写抽象角色
  • 同10.2.4的抽象角色接口
2.编写真实角色
  • 在10.2.4的一个真实角色基础上,再添加一个真实角色UserServiceImpl2
// 用户服务接口的实现类(第二个真实角色)
public class UserServiceImpl2 implements UserService {
    
    // 重写增删改查方法
    public void add() {
        System.out.println("增加一个用户信息");
    }
    
    public void delete() {
        System.out.println("删除一个用户信息");
    }
    
    public void update() {
        System.out.println("修改加一个用户信息");
    }
    
    public void query() {
        System.out.println("查询一个用户信息");
    }
    
}
3.编写动态代理工具类
  • 同10.2.4动态代理工具类
4.模拟客户端访问代理角色
public class Client {
    public static void main(String[] args) {
        // 获取多个真实角色
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceImpl2 userService2 = new UserServiceImpl2();
        // 获取动态代理工具类(中介所): 此时代理角色中介还未出现
        ProxyInvocationHandler pih= new ProxyInvocationHandler();
        ProxyInvocationHandler pih2= new ProxyInvocationHandler();
        // 设置多个要代理的对象:通过调用动态代理工具类的setTarget方法来实现代理角色的注入
        pih.setTarget(userService);
        pih2.setTarget(userService2);
        // 动态生成多个代理类:此时代理角色中介才出现
        UserService proxy = (UserService) pih.getProxy();
        UserService proxy2 = (UserService) pih2.getProxy();
        // 多个代理类实例代理执行业务
        proxy.add();
        proxy2.delete();
    }
}
5.测试结果

在这里插入图片描述

好了,今天的有关代理模式的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!


参考视频链接:https://www.bilibili.com/video/BV1WE411d7Dv(【狂神说Java】Spring5最新教程IDEA版通俗易懂)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值