回答
Mybatis 的运行涉及到 Executor、StatementHandler、ParameterHandler、ResultSetHandler 等组件,插件的工作原理就是在这些组件执行过程中,插入一些自定义的代码逻辑。
MyBatis 插件机制允许用户自定义拦截器(实现Interceptor
接口),以在执行 SQL 的各个阶段进行额外的处理。
实现机制
Mybatis 使用 JDK 动态代理,为目标对象生成代理对象。从而在目标对象方法调用时被拦截器拦截处理。
public class Plugin implements InvocationHandler {
/**
* 创建代理对象
*/
public static Object wrap(Object target, Interceptor interceptor) {
// 获取拦截器关注的类和方法签名的映射
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
// 获取目标对象实现的所有接口
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
// JDK 动态代理创建目标对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 从签名 Map 获取方法
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
// 存在则执行拦截器逻辑
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
扩展
Mybatis 插件的实现步骤
- 定义拦截器:实现
Interceptor
接口。
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 添加自定义的拦截逻辑
System.out.println("Before executing SQL");
Object returnValue = invocation.proceed(); // 继续执行原方法
System.out.println("After executing SQL");
return returnValue;
}
@Override
public Object plugin(Object target) {
// 创建代理对象。
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 设置插件属性
}
}
- 配置拦截器:在 MyBatis 配置文件中注册拦截器。
方法一
<configuration>
<plugins>
<plugin interceptor="com.damingge.interceptor.MyInterceptor"/>
</plugins>
</configuration>
方法二
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
// 设置自定义插件
Interceptor[] plugins = {new MyInterceptor()};
sessionFactoryBean.setPlugins(plugins);
return sessionFactoryBean.getObject();
}
}
备注:在 MyBatis 中,支持多个拦截器。拦截器之间的执行顺序由它们在配置中的声明顺序决定。
- 拦截目标方法:在拦截器中定义要拦截的方法和逻辑。
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Intercepts
和 @Signature
注解
type
:拦截的对象类型。method
:拦截的方法名称。args
:方法的参数类型。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。