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 {
@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
。代理类在调用接口方法时,会转发调用到 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 {
/**
* 测试动态代理的地方
*/
@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、工作原理
- 生成子类: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 {
@Test
public void testCGLIB() {
UserService userServiceProxy = new UserServiceProxyFactory().getUserServiceProxy();
userServiceProxy.update();
}
}
输出结果: