Mybatis—代理设计模式
文章目录
- Mybatis---代理设计模式
- 一、什么是代理设计模式
- 二、静态代理
- 1、定义
- 2、结构
- 3、示例
- 三、动态代理
- 1、定义
- 2、newProxyInstance ()方法
- 3、示例
- 四、CGLIB代理
- 1、引入
- 2、定义
- 3、工作原理
- 4、示例
一、什么是代理设计模式
首先需要知道什么是设计模式:
设计模式:为了实现某一个功能 前人总结出的一个好的方法和步骤
代理设计模式: 一种结构型设计模式,主要用于为其他对象提供一种代理,以控制对这个对象的访问。代理模式通常用于延迟对象的加载、控制对资源的访问,或者在不修改原始类的情况下对功能进行扩展。
代理的设计模式 是为了解决什么问题呢?
他可以动态的监控一个类中 方法在什么时候 执行 以及可以在方法执行的前后 动态植入我们的代码
注意:静态代理 和 动态代理都有一个代理的前提 就是我们的被代理的类 一定要实现接口 或者自己就是接口
说白了 代理的设计模式 最终的目的 就是对类中的方法进行增强
二、静态代理
1、定义
静态代理 是代理模式的一种实现方式,在编译时通过明确的代理类来控制对真实对象的访问。静态代理通常需要手动编写代理类,它在功能上可以对原有对象的功能进行扩展,而不需要修改原始类的代码。与动态代理相比,静态代理的结构较为简单,但代码的可复用性不高。
2、结构
- 接口(Subject):定义了代理对象和真实对象的通用行为,代理对象和真实对象都需要实现该接口。
- 真实对象(Real Subject):实际执行业务逻辑的对象。
- 代理对象(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 {@Testpublic 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
。代理类在调用接口方法时,会转发调用到 InvocationHandler
的 invoke()
方法。
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 {/*** 测试动态代理的地方*/@Testpublic 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? 爹里面的methodtry {Method method = interfaces.getMethod("update");//接下来怎么做呢?this.invocationHandler.invoke(this, method, null);} catch (Throwable e) {throw new RuntimeException(e);}}public void add() {//这里怎么做呢?//通过父亲(接口) 去找他爹里面相同名字的方法(反射)//这个method是谁里面的method? 爹里面的methodtry {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、工作原理
- 生成子类:CGLIB 通过 ASM 字节码操作框架,生成目标类的子类来实现代理。
- 方法拦截: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 {@Testpublic void testCGLIB() {UserService userServiceProxy = new UserServiceProxyFactory().getUserServiceProxy();userServiceProxy.update();}
}
输出结果: