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

59 篇文章 0 订阅

Mybatis—代理设计模式

一、什么是代理设计模式

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

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

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

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

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

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

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

二、静态代理

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();
    }
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值