Mybatis---代理设计模式(超详细)

Mybatis—代理设计模式

文章目录

  • Mybatis---代理设计模式
    • 一、什么是代理设计模式
    • 二、静态代理
      • 1、定义
      • 2、结构
      • 3、示例
    • 三、动态代理
      • 1、定义
      • 2、newProxyInstance ()方法
      • 3、示例
    • 四、CGLIB代理
      • 1、引入
      • 2、定义
      • 3、工作原理
      • 4、示例

一、什么是代理设计模式

首先需要知道什么是设计模式:

设计模式:为了实现某一个功能 前人总结出的一个好的方法和步骤

代理设计模式: 一种结构型设计模式,主要用于为其他对象提供一种代理,以控制对这个对象的访问。代理模式通常用于延迟对象的加载、控制对资源的访问,或者在不修改原始类的情况下对功能进行扩展。

代理的设计模式 是为了解决什么问题呢?

他可以动态的监控一个类中 方法在什么时候 执行 以及可以在方法执行的前后 动态植入我们的代码

注意:静态代理 和 动态代理都有一个代理的前提 就是我们的被代理的类 一定要实现接口 或者自己就是接口

说白了 代理的设计模式 最终的目的 就是对类中的方法进行增强

二、静态代理

1、定义

静态代理 是代理模式的一种实现方式,在编译时通过明确的代理类来控制对真实对象的访问。静态代理通常需要手动编写代理类,它在功能上可以对原有对象的功能进行扩展,而不需要修改原始类的代码。与动态代理相比,静态代理的结构较为简单,但代码的可复用性不高。

2、结构

  1. 接口(Subject):定义了代理对象和真实对象的通用行为,代理对象和真实对象都需要实现该接口。
  2. 真实对象(Real Subject):实际执行业务逻辑的对象。
  3. 代理对象(Proxy):持有真实对象的引用,并通过它来控制对真实对象的访问。代理对象可以在调用真实对象的方法前后添加额外的操作。

3、示例

需求:就是Service类中所有方法在执行之前都需要 输出一句话 打开事务;在所有方法执行完成之后 我们都需要输出一句话 关闭和提交事务。

3.1、编写接口

public interface IUserService {
    /**
     * 更新的方法
     */
    void update();

    /**
     * 添加数据的方法
     */
    void add();
}

3.2、编写接口实现类

package com.qfedu.edu.proxy.static1;

/**
 * @author xiaobobo
 * @title: UserService
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个类就成为被代理的类
 * 现在我们有一个要求:
 *    就是Service类中所有方法在执行之前都需要 输出一句话 打开事务
 *    在所有方法执行完成之后  我们都需要输出一句话  关闭和提交事务
 * @date 2024/9/3  14:45
 */
public class UserService implements IUserService {

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}

3.3、编写代理类

package com.qfedu.edu.proxy.static1;

import static com.qfedu.edu.proxy.utils.TransactionUtils.*;

/**
 * @author xiaobobo
 * @title: UserServiceProxy
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 静态代理的第一步:编写一个代理类和被代理的类实现相同的接口
 * @date 2024/9/3  14:49
 */
public class UserServiceProxy implements IUserService {

    //静态代理的第二步:在代理类中维护被代理类的对象
    private IUserService userService;

    //静态代理的第三步:在构造器中去实例化这个成员变量
    public UserServiceProxy(IUserService userService) {
        this.userService = userService;
    }

    //静态代理的第四步:在代理中的方法中 调用被代理类 相同名字的方法
    public void update() {
        beginTransaction();
        this.userService.update();
        closeCommitTransaction();
    }

    public void add() {
        beginTransaction();
        this.userService.add();
        closeCommitTransaction();
    }
}

3.4、编写测试类

public class Test001 {
    @Test
    public void testProxy() {
     UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
     userServiceProxy.add();
    }
}

输出结果:

在这里插入图片描述

三、动态代理

1、定义

允许在运行时动态生成代理类,而不是在编译时确定。这在 Java 等支持反射的编程语言中非常常见。代理模式的核心思想是通过代理对象来控制对目标对象的访问,而动态代理则进一步增强了这种控制的灵活性。

理解动态代理又名 JDK代理 简单的说 就是整个代理的过程JDK帮你实现了 你直接用就可以了…

经验: 在 Java 中,动态代理通常通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口实现。使用动态代理,代理类不需要预先定义,而是通过 Proxy.newProxyInstance() 方法在运行时动态创建。

2、newProxyInstance ()方法

动态代理的核心方法 Proxy.newProxyInstance() , 它用于在运行时生成代理对象。它允许你创建实现一个或多个接口的代理对象,而无需预先定义代理类。这个方法属于 java.lang.reflect.Proxy 类。

参数说明

第一个参数是类加载器 :固定写法 被代理的类.class.getClassLoader
第二个参数是被代理的类实现的接口
1、如果被代理的是类
类.class.getInterfaces()
2、如果被代理的是接口
new Class[]{接口.class}
第三个参数:回调函数
JDK代理实际上生成的是 接口的实现类 兄弟关系
在JDK代理中第三个参数是最重要的 因为可以监控方法在什么时候执行

工作原理

当你调用 newProxyInstance() 方法时,它会在运行时生成一个代理类,该类实现了指定的接口,并将所有方法调用委托给 InvocationHandler。代理类在调用接口方法时,会转发调用到 InvocationHandlerinvoke() 方法。

3、示例

2.1、编写接口

package com.qfedu.edu.proxy.dynamic;

/**
 * @author xiaobobo
 * @title: IUserService
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个是被代理的类实现的接口
 * @date 2024/9/3  14:44
 */
public interface IUserService {
    /**
     * 更新的方法
     */
    void update();

    /**
     * 添加数据的方法
     */
    void add();
}

2.2、编写被代理类

public class UserService implements IUserService {

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}

2.3、测试代理类

package com.qfedu.edu.proxy.dynamic;

import com.qfedu.edu.proxy.utils.TransactionUtils;
import org.junit.Test;

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

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/3  15:19
 */
public class Test001 {

    /**
     * 测试动态代理的地方
     */
    @Test
    public void testDynamicProxy() {
        //首先生成代理类对象
        /**
         * 第一个参数是类加载器 :固定写法  被代理的类.class.getClassLoader
         * 第二个参数是被代理的类实现的接口
         *       1>、如果被代理的是类
         *           类.class.getInterfaces()
         *       2>、如果被代理的是接口
         *           new Class[]{接口.class}
         * 第三个参数:回调函数
         * JDK代理实际上生成的是 接口的实现类 兄弟
         * 在JDK代理中第三个参数是最重要的 因为可以监控方法在什么时候执行
         */
        IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
                UserService.class.getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 这个方法就是监控被代理类中 方法在什么时候执行的回调函数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        System.out.println("当前执行的方法的名字是:" + name);
                        TransactionUtils.beginTransaction();
                        //放行执行到目标类中去(这个类的实例 应该是目标类对象)
                        Object invoke = method.invoke(new UserService(), args);
                        TransactionUtils.closeCommitTransaction();
                        return invoke;
                    }
                });
        userServiceProxy.update();
    }

}

2.4、模拟生成的代理类对象

package com.qfedu.edu.proxy.dynamic;

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

/**
 * @author xiaobobo
 * @title: UserServiceProxy
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 反推出这个代理类 应该长啥样?
 * @date 2024/9/3  15:30
 */
public class UserServiceProxy implements IUserService {

    //相当于把这个接口传递过来了(这个相当于是爹的这个class对象)
    private Class interfaces;

    private InvocationHandler invocationHandler;

    public UserServiceProxy(Class interfaces, InvocationHandler invocationHandler) {
        this.interfaces = interfaces;
        this.invocationHandler = invocationHandler;
    }

    public void update() {
        //这里怎么做呢?
        //通过父亲(接口) 去找他爹里面相同名字的方法(反射)
        //这个method是谁里面的method? 爹里面的method
        try {
            Method method = interfaces.getMethod("update");
            //接下来怎么做呢?
            this.invocationHandler.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

    }

    public void add() {
         //这里怎么做呢?
        //通过父亲(接口) 去找他爹里面相同名字的方法(反射)
        //这个method是谁里面的method? 爹里面的method
        try {
            Method method = interfaces.getMethod("add");
            //接下来怎么做呢?
            this.invocationHandler.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}

输出结果:
在这里插入图片描述

四、CGLIB代理

1、引入

有个问题:

就是不论咋们的静态代理 还是 CGLIB代理 都有一个代理的前提

这个代理的前提是:被代理的类 必须实现接口 或者本身就是接口

假设现在有一个类 没有实现接口 但是我们依然想给他进行功能的拓展 我们怎么办呢?

于是CGLIB代理就应运而生了…

记住CGLIB代理的代理类 肯定不需要我们去实现了 只是需要我们去获取代理类对象就可以了 跟JDK代理是一样的

但是这个CGLIB代理类生成的是 子类 生成的是 被代理类的子类

2、定义

CGLIB 代理 是另一种实现代理设计模式的技术,与 Java 自带的动态代理 (Proxy.newProxyInstance()) 不同,CGLIB 是通过生成目标类的子类来创建代理对象,而不是基于接口代理。这使得 CGLIB 能够代理 没有实现接口的类,解决了 JDK 动态代理只能代理接口的局限性。

CGLIB 全称是 Code Generation Library,它在运行时生成字节码,并动态创建目标类的子类。因此,CGLIB 代理的本质是通过继承来实现的。

3、工作原理

  1. 生成子类:CGLIB 通过 ASM 字节码操作框架,生成目标类的子类来实现代理。
  2. 方法拦截:CGLIB 使用 MethodInterceptor 来拦截对目标方法的调用,类似于 JDK 动态代理中的 InvocationHandler

4、示例

4.1、导包

<!--        这个就是咋们的CGLIb代理需要的这个包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>

4.2、编写被代理类

public class UserService{

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}

4.3、编写工厂

package com.qfedu.edu.proxy.cglib;

import com.qfedu.edu.proxy.utils.TransactionUtils;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author xiaobobo
 * @title: UserServiceProxyFactory
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个类的主要作用是进行CGLIB代理类的生产
 * @date 2024/9/4  9:29
 */
public class UserServiceProxyFactory implements MethodInterceptor {
    /**
     * 这个方法的主要作用就是生成咋们的这个代理类对象
     *
     * @return
     */
    public UserService getUserServiceProxy() {
        Enhancer enhancer = new Enhancer();
        //设置他爹是谁
        enhancer.setSuperclass(UserService.class);
        //设置这个拦截对象
        enhancer.setCallback(this);
        return (UserService) enhancer.create();
    }

    /**
     * 这个方法主要就是为了实现这个方法执行时候的拦截的
     *
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里你就可以对方法进行增强了
        TransactionUtils.beginTransaction();
        Object invoke = method.invoke(new UserService(), objects);
        TransactionUtils.closeCommitTransaction();
        return invoke;
    }
}

4.4、编写测试

package com.qfedu.edu.proxy.cglib;

import org.junit.Test;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/4  9:37
 */
public class Test001 {
    @Test
    public void testCGLIB() {
        UserService userServiceProxy = new UserServiceProxyFactory().getUserServiceProxy();
        userServiceProxy.update();
    }
}

输出结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/872599.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI基础 L1 Introduction to Artificial Intelligence

什么是AI Chinese Room Thought Experiment 关于“强人工智能”的观点&#xff0c;即认为只要一个系统在行为上表现得像有意识&#xff0c;那么它就真的具有理解能力。 实验内容如下&#xff1a; 假设有一个不懂中文的英语说话者被关在一个房间里。房间里有一本用英文写的中…

鸿蒙开发基础知识-页面布局【第四篇】

1.类型转换 2.交互点击事件 3.状态管理 4.forEch渲染和右上角图标 测试案例 Stack 层叠布局一个生肖卡 5. 动画展示图片 6. Swiper 轮播组件的基本使用 图片等比显示 aspectRatio&#xff08;&#xff09;

TikTok直播为什么要用独立IP

TikTok直播作为一种受欢迎的社交媒体形式&#xff0c;吸引了越来越多的用户和内容创作者。在进行TikTok直播时&#xff0c;选择使用独立IP地址是一种被广泛推荐的做法。本文将探讨为什么在TikTok直播中更推荐使用独立IP&#xff0c;并解释其优势和应用。 独立IP是指一个唯一的互…

sheng的学习笔记-AI-半监督聚类

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 半监督学习&#xff1a;sheng的学习笔记-AI-半监督学习-CSDN博客 聚类&#xff1a;sheng的学习笔记-AI-聚类(Clustering)-CSDN博客 均值算法&#xff1a;sheng的学习笔记-AI-K均值算法_k均值算法怎么算迭代两次后的最大…

Linux-(系统启动、用户管理)

目录 前言 关机&重启命令 基本介绍 注意细节 用户登录和注销 注意&#xff1a; 用户管理 基本介绍 添加用户 指定/修改密码 删除用户 查询用户信息 切换用户 查看当前用户登录用户 用户组 新增组 删除组 查看所有组 修改用户所属组 创建用户时指定用户…

超声波微型气象仪

超声波微型气象仪是一种便携式的气象观测仪器&#xff0c;可以测量温度、湿度、气压和风速等气象参数。其使用方法如下&#xff1a; 打开仪器电源&#xff0c;并确保仪器已经预热完成。将仪器放置在待测环境中&#xff0c;确保避免直接阳光照射和强风的影响。确定仪器与待测气…

110001安庆巡检_工艺巡检

安庆巡检_工艺巡检 一. 工艺配置二. 点检计划三. 点检任务四. 复检任务1. 复检列表1.1 页面展示 2. 复检任务下发2.1 操作说明2.2 业务说明2.3 表关联说明ps_recheck_task工艺工序参数_复检详情表 3. 复检详情2.1 获取参数点检详情2.2 获取复检详情列表 4. app端复检任务提交4.…

HTML的块级元素与行内元素

在HTML中&#xff0c;元素可以分为两大类&#xff1a;块级元素&#xff08;block-level elements&#xff09;和行内元素&#xff08;inline elements&#xff09;。这两种类型的元素在网页布局和呈现中扮演着不同的角色。 块级元素&#xff08;Block-level Elements&#xff…

免费申请aws一年免费服务器使用教程

由于近期要测试一个公网项目&#xff0c;对比之下&#xff0c;选择了aws服务器&#xff0c;免费使用一年。 准备&#xff1a;一个visa信用卡即可&#xff0c;需要一个外网邮箱&#xff08;我这边使用的hotmail&#xff09; 注册的步骤不再赘述&#xff0c;切记几个点&#xff0…

智 能 合 约

1. 智能合约的历史 智能合约最初是由 Nick Szabo 在 20 世纪 90 年代后期的一篇名为 Formalizing and Securing Relationships on Public Networks(《公共网络上关系的格式化和安全保护》&#xff09;的文章中提出的&#xff0c;但是 20 年之后&#xff0c;比特币的发明和区块链…

Qt QGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小_图片查看

QtQGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小 头文件&#xff1a; #ifndef TIMGWIDGET_H #define TIMGWIDGET_H#include <QGraphicsItem> #include <QMainWindow> #include <QObject> #include <QWidget>// class TImgWidget : pu…

【重构获得模式 Refactoring to Patterns】

重构获得模式 Refactoring to Patterns 面向对象设计模式是“好的面向对象设计”&#xff0c;所谓“好的面向对象设计”指的是那些可以满足“应对变化&#xff0c;提高复用”的设计。 现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点&#xff0c;然后…

Opencv中的直方图(1)计算反向投影直方图函数calcBackProject()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算直方图的反向投影。 cv::calcBackProject 函数计算直方图的反向投影。也就是说&#xff0c;类似于 calcHist&#xff0c;在每个位置 (x, y)…

12道经典性能测试人员面试题

1.性能测试包含了哪些软件测试&#xff08;至少举出3种&#xff09;&#xff1f; 参考答案&#xff1a;负载测试、压力测试、容量测试。 负载测试&#xff08;Load Testing&#xff09;&#xff1a;负载测试是一种主要为了测试软件系统是否达到需求文档设计的目标&#xff0c…

Spring MVC 八股文

目录 重点 SpringMVC的工作原理 Spring MVC 拦截器 Spring MVC 的拦截器和 Filter 过滤器有什么差别&#xff1f; 基础 什么是SpringMVC SpringMVC的优点 Spring MVC的核心组件 Spring MVC的常用注解由有哪些 Controller 注解有什么用 重点 SpringMVC的工作原理 1、客…

【舍入,取整,取小数,取余数丨Excel 函数】

数学函数 1、Round函数 Roundup函数 Rounddown函数 取整&#xff1a;(Int /Trunc)其他舍入函数&#xff1a; 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字&#xff0c;保留几位小数)&#xff08;四…

Word快速重复上一步操作的三种高效方法

在日常工作、学习和生活中&#xff0c;我们经常需要执行一系列重复性的操作。这些操作可能简单如复制粘贴、调整图片大小&#xff0c;也可能复杂如编辑文档、处理数据等。为了提高效率&#xff0c;掌握快速重复上一步操作的方法显得尤为重要。本文将介绍三种高效的方法&#xf…

Carla自动驾驶仿真十:Carlaviz三维可视化平台搭建

文章目录 前言一、环境准备1、docker安装2、websocket-client安装3、carlaviz代码下载 二、carlaviz使用1、打开carla客户端2、输入启动命令3、进入carlaviz4、修改manual_control.py脚本5、运行manual_control.py脚本6、运行carlaviz官方脚本&#xff08;推荐&#xff09; 前言…

【2024最新】Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够了!

前言 本文罗列了了python零基础入门到精通的详细教程&#xff0c;内容均以知识目录的形式展开。 第一章&#xff1a;python基础之markdown Typora软件下载Typora基本使用Typora补充说明编程与编程语言计算机的本质计算机五大组成部分计算机三大核心硬件操作系统 第二章&…

【计算机网络】浏览器输入访问某网址时,后台流程是什么

在访问网址时&#xff0c;后台的具体流程可以因不同的网站、服务器和应用架构而异。 实际过程中可能还涉及更多的细节和步骤&#xff0c;如缓存处理、重定向、负载均衡等。 此外&#xff0c;不同的网站和应用架构可能会有不同的实现方式和优化策略。 部分特定网站或应用&#x…