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

news2024/12/24 20:46:33

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.coloradmin.cn/o/2112237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据-121 - Flink Time Watermark 详解 附带示例详解

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Unity(2022.3.41LTS) - UI详细介绍- Toggle(切换)

目录 零.简介 一、基本功能 二、属性和设置 三、使用方法 四、优化和注意事项 零.简介 在 Unity 中&#xff0c;Toggle 是一种常用的 UI 组件&#xff0c;用于表示一个布尔值的状态&#xff0c;类似于复选框。 一、基本功能 状态切换&#xff1a;Toggle 有两种状态&…

sqli-lab靶场学习(一)——Less1-4

前言 最近一段时间想切入安全领域&#xff0c;因为本身有做数据库运维工作&#xff0c;就打算从sql注入方向切入。而sql注入除了学习日常书本上的概念外&#xff0c;需要有个实践的环境&#xff0c;刚好看到sqli-lab这个靶场&#xff0c;就打算先用这个来学习。 安装部署 网上…

软考-软件设计师(UML习题)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

梯度弥散问题及解决方法

梯度弥散问题及解决方法 简要阐述梯度弥散发生的原因以及现象针对不同发生原因有什么解决方案1. 使用ReLU及其变体激活函数2. 权重初始化3. 批量归一化(Batch Normalization)4. 残差连接(Residual Connections)5. 梯度裁剪(Gradient Clipping)简要阐述梯度弥散发生的原因…

挂轨巡检机器人在发电厂与煤矿皮带机场景的应用

一、引言 在发电厂和煤矿等重工业领域&#xff0c;皮带机作为关键设备&#xff0c;其运行状态直接关系到生产效率和安全。然而&#xff0c;传统的人工巡检方式不仅效率低下&#xff0c;还存在安全隐患。随着智能巡检技术的不断发展&#xff0c;杭州旗晟智能科技有限公司推出的…

UE5.3_跟一个插件—Socket.IO Client

网上看到这个插件,挺好! 项目目前也没有忙到不可开交,索性跟着测一下吧: 商城可见,售价72.61人民币! 但是,git上有仓库哦,免费!! 跟着链接先准备起来: Documentation: GitHub - getnamo/SocketIOClient-Unreal: Socket.IO client plugin for the Unreal Engin…

(仓颉) 仓颉语言入门

文章目录 ⭐前言&#x1f520;1 基本概念&#x1f524;Hello World&#x1f524;标识符&#x1f524;内置类型&#x1f524;if表达式&#x1f524;for表达式&#x1f524;while表达式&#x1f524;demo 手动计算Π &#x1f520;2 函数&#x1f524;普通函数&#x1f524;lambd…

【LeetCode】05.最长回文子串

题目要求 解题思路 这一类型&#xff08;回文子串&#xff09;主要有两种解决方法&#xff0c;一种是动态规划&#xff0c;另一种是中心拓展算法。 动态规划&#xff1a; 本质问题就是在i-j区间是不是回文的。这样的话我们在 i 和 j 位置的值相等时&#xff0c;判断如下三种情…

最直接显示 ubuntu 版本号的命令

有时候去看ubuntu版本号&#xff0c;去网上查&#xff0c;很多文章都列出一堆命令&#xff0c;复制命令运行一下&#xff0c;都是打印一些不相关的信息&#xff0c;我只是想看ubuntu版本号而已&#xff0c;能否直接列出版本号就可以了。 有&#xff0c;下面这条命令就是直接的…

STM32+ESP8266+MQTT协议连接阿里云实现温湿度上传

前期步骤: ESP8266下载固件→连接阿里云-CSDN博客 keil文件&#xff1a;大家可以直接下载&#xff0c;也可以在后台私信我获取 《STM32ESP8266MQTT协议连接阿里云实现温湿度上传》 keil文件源码 一、代码修改部分 1、mqtt.h文件中的修改 2、wifi.h文件中的修改 3、main.…

Matplotlib 画布及Figure文字注释添加详解

text() 若要给画布对象或者Figure对象添加注释,常用的为text()方法,分别可以用Figure对象.text()或者画布对象.text()添加文字注释 x, y, s, fontdictNone 上述为text()函数的参数x,y为注释所在的坐标,s为要添加的注释,fontdict为字体样式字典,键包括常见的样式,如‘color’…

微积分直觉:隐含微分

目录 一、介绍 二、梯子问题 三、结论 四、一个额外的例子 一、介绍 让我们想象一个半径为 5 的圆&#xff0c;以 xy 平面为中心。现在假设我们想在点 &#xff08;3,4&#xff09; 处找到一条切线到圆的斜率。 好吧&#xff0c;为了做到这一点&#xff0c;我们必须非常接近圆和…

Qt-布局管理

布局管理 Qt布局系统提供了一种简单而强大的方式&#xff0c;可以自动在窗口组件中排列子窗口组件&#xff0c;以确保它们充分利用可用空间。 介绍 Qt包含了一组布局管理类&#xff0c;用于描述窗口组件在应用程序用户界面中的布局方式。当可用空间发生变化时&#xff0c;这…

Typora 写文章时修改图片保存位置

文章目录 为什么要修改图片位置修改图片位置1.打开偏好设置2.修改路径3.再写文章发现图片已经变成了相对路径 为什么要修改图片位置 默认位置不好找 如何修改呢&#xff1f; 修改图片位置 1.打开偏好设置 文件 > 偏好设置 2.修改路径 图像 > 插入图片时 按照下图…

C# 集合(Collection)

集合&#xff08;Collection&#xff09;类是专门用于数据存储和检索的类。这些类提供了对栈&#xff08;stack&#xff09;、队列&#xff08;queue&#xff09;、列表&#xff08;list&#xff09;和哈希表&#xff08;hash table&#xff09;的支持。大多数集合类实现了相同…

超市售货管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;会员管理&#xff0c;供应商信息管理&#xff0c;商品管理&#xff0c;出入库管理&#xff0c;公告管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#xff0c;公告&#xff0c;…

Midjourney 随机风格 (Style Random),开启奇幻视觉之旅

作者:老余捞鱼 原创不易,转载请标明出处及原作者。 写在前面的话: Midjourney 最近推出了 "Style Random"(随机风格),这项功能可以让我们使用独特的随机 sref 代码创建图像,从而每次都能获得不同的美感。通过对这些功能的探索和尝试,我发现了一些很棒…

vscode安装使用plantuml插件

使用 VSCode 插件 如果你在 Visual Studio Code 中使用 PlantUML 插件&#xff0c;你可以按照以下步骤生成图片&#xff1a; 安装 PlantUML 插件&#xff1a; 在 VSCode 的扩展市场中搜索并安装 PlantUML 插件。 配置插件&#xff1a; 打开设置&#xff0c;确保插件配置正确。…

【Unity小工具】Image组件宽度、高度自适应

Unity开发中&#xff0c;用同一个Image进行动态加载不同尺寸的图片&#xff0c;在显示上会有形变此工具可以进行Image的宽度、高度自适应 实现原理 获取Image原始尺寸&#xff08;sizeDelta&#xff09;获取图片原始尺寸&#xff08;spriteSizeDelta&#xff09;公式&#xff…