当前位置: 首页 > news >正文

Mybatis插件

插件使用

动手实现plugin

首先我们需要实现一下这个Interceptor,其中plugin和setProperties方法可以不实现,plugin是因为已经有了完善的逻辑,而setProperties,如果不需要在intercept()中使用属性,也可以不设置。然后在实现类中完成注解配置

public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;default Object plugin(Object target) {return Plugin.wrap(target, this);}default void setProperties(Properties properties) {// NOP}}

以下是一个实现demo

@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class}
), @Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class testMybatisInterceptor implements Interceptor {private Properties properties;@Overridepublic Object intercept(Invocation invocation) throws Throwable {return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {this.properties = properties;}public Properties getProperties() {return properties;}}

向容器注入该interceptor

可以通过两种方式
一种是@Bean注入

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {return new testMybatisInterceptor();
}

另一种是xml注入,目前比较少用了

<plugins><plugin interceptor="com.testMybatisInterceptor"></plugin>
</plugins>

拦截器原理

public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

拦截器的添加

初始化的添加

Configuration是mybatis的核心初始化数据承装容器,在一开始它就会创建一个InterceptorChain,后期所有添加和过滤都是在这个InterceptorChain上操作的。

public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

@Bean

@Bean只是本人的猜测,mybatis应该会添加一个beanPostProcessor,对于当前bean是否拥有@interceptors注解进行扫描,如果有,就会把这个bean放入Configuration

XML

在解析XML的时候,就会把拦截器添加进configuration

  private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}}

初始化Handler时的设置

在完成newParameter的时候,就会对Handler进行拦截器封装。封装的过程就是使用代理模式,invokeHandler就是

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,parameterObject, boundSql);return (ParameterHandler) interceptorChain.pluginAll(parameterHandler);}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,resultHandler, boundSql, rowBounds);return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);}public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,rowBounds, resultHandler, boundSql);return (StatementHandler) interceptorChain.pluginAll(statementHandler);}public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}return (Executor) interceptorChain.pluginAll(executor);}

在pluginAll中,会对每一个Interceptor进行接口拦截

  public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}

target就是以上某种Handler或者Executor,interceptor就是当前遍历的执行器

  public static Object wrap(Object target, Interceptor interceptor) {Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));}return target;}

其中getSignatureMap,就是把注解转化为map,供以后调用使用,里面的key就是type,而value就是prepare(Connection,Integer)这个方法。

@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class}
)})

在invoke方法中,会先检查这个方法是否通过注解注册过,如果是注册的方法,就会调用intercept方法,并且把当前handler,当前方法,和方法参数返回给interceptor完成后续业务操作。

  @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {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);}}

处理拦截器

对应Handler在调用方法时,会调用intercept(),如果通过,会调用method.invoke()进行真正的代码执行,如果在拦截器模式下,由于是代理模式套代理模式层层循环,所以实际上是调用了第二个代理器的intercept(),直到完全调用完,才会进入最内层的被代理对象的源方法。

@Override
public Object intercept(Invocation invocation) throws Throwable {return invocation.proceed();
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);
}

相关文章:

Mybatis插件

插件使用 动手实现plugin 首先我们需要实现一下这个Interceptor&#xff0c;其中plugin和setProperties方法可以不实现&#xff0c;plugin是因为已经有了完善的逻辑&#xff0c;而setProperties&#xff0c;如果不需要在intercept()中使用属性&#xff0c;也可以不设置。然后…...

计算机学科专业基础综合科目(408)

文章目录408 第一章 数据结构数据是客观事物的符号表示&#xff0c;是对现实世界的事物采用计算机能够识别&#xff0c;存储和处理的形式进行描述的符号的集合。 数据元素是数据的基本单位。一个数据元素由若干个数据项组成。数据项又成为简单数据项及复合数据项两种。简单数据…...

centos7安装教程

1.点击文件–新建虚拟机 2.根据图片一直下一步或者做一些改动 3. 点击自定义硬件&#xff0c;点击浏览选中下载好的ISO文件 4.配置完成后启动虚拟机 5.选择语言&#xff0c;中英文都可&#xff0c;按需求选择 6.进行设置目标位置&#xff0c;配置分区 7.选择网络和主机名 8.配置…...

Kafka 重平衡

Kafka 重平衡协调者RebalanceRebalance 条件Rebalance 避免Rebalance : 让单 Group 下所有的 Consumer 怎么消费订阅主题的所有分区Rebalance 时 , 所有 Consumer 要共同参与 (无法消费)&#xff0c;在协调者 (Coordinator) 协调下&#xff0c;完成订阅主题分区的分配 协调者…...

PTA:L1-022 奇偶分家、L1-023 输出GPLT、L1-024 后天(C++)

目录 L1-022 奇偶分家 问题描述&#xff1a; L1-023 输出GPLT 问题描述&#xff1a; 实现代码&#xff1a; L1-024 后天 问题描述&#xff1a; 实现代码&#xff1a; 简单题&#xff0c;没写题解&#xff0c;看代码就能看懂 L1-022 奇偶分家 问题描述&#xff1a; 给…...

IDEA插件开发入门.02

前言许久没更新IDEA插件开发系列了。最近刚好在汇总日常开发中常见的代码“异味”&#xff0c;共享文档复制黏贴略显麻烦&#xff0c;所以想着是否可以搞一个IDEA插件来帮忙收集常见代码&#xff0c;毕竟IDEA作为后端程序员必备的开发工具&#xff0c;显然会方便很多。于是&…...

如何用 23 种编程语言说“Hello World”

在编程的世界里&#xff0c;" Hello World " 往往是开发者开始学习一种新语言时写的第一个程序。这个简单的程序会将 “Hello World“ 输出在我们的屏幕上。看似很简单的行为&#xff0c;实际上对于每一个新学习编程语言的人来说&#xff0c;它代表着新的起点。那么&…...

【Linux快速入门】文件目录操作

文章目录概念1. Linux文件系统概述2. Linux文件目录结构3. Linux文件和目录操作3.1 文件操作3.1.1 创建文件3.1.2 复制文件3.1.3 移动文件3.1.4 删除文件3.1.5 查看文件3.1.6 输出指令3.1.7 >和>>指令3.2 目录操作3.2.1 创建目录3.2.2 复制目录3.2.3 移动目录3.2.4 删…...

字体反爬慢慢总结破解方式

什么是字体反爬 网页开发者自己创造一种字体&#xff0c;因为在字体中每个汉字都有其代号&#xff0c;那么以后再网页中不会直接显示这个文字的效果。而是显示其代号&#xff0c;因此即使获取了网页的文本内容。也只是获取到文字的代号&#xff0c;而不是文字本身。 简单来说&…...

Kafka 位移提交

Kafka 位移提交自动提交手动提交Consumer 的消费位移 : 记录 Consumer 下一条消息的消费位移 如 : Consumer 已消费 5 条消息 (位移: 0 - 4) , 此时 Consumer 位移 5 : 指向下一条消息的位移 提交位移 (Committing Offsets) : Consumer 向 Kafka 汇报位移数据 Consumer 能同…...

kubernetes--监控容器运行时:Falco

目录 Falco介绍 Falco架构 Falco的安装 告警规则示列 威胁场景测试&#xff1a; 监控容器创建的不可信任进程&#xff08;自定义规则&#xff09; Falco支持五种输出告警方式falco.yaml&#xff1a; Falco告警集中化展示&#xff1a; Falco介绍 Falco是一个Linux安全工具…...

HTTP协议详解(上)

目录 前言&#xff1a; 认识URL HTTP协议方法 通过Fiddler抓包 GET和POST之间典型区别 header详解 HTTP响应状态码 常见状态码解释 状态码分类 HTTP协议报文格式 小结&#xff1a; 前言&#xff1a; HTTP协议属于应用层协议&#xff0c;称为超文本传输协议&#xff…...

java性能-原生内存-内存分析

原生内存最佳实践 内存占用 jVM使用的原生内存和堆内存总和就是一个应用程序的总内存——操作系统角度 jvm启动时候加载的类路径下的jar文件相关的内存和系统其他进程共享资源的可能 测量内存占用 线程是个例外——每当创建一个线程操作系统都会分配一些原生内存存储线程栈…...

c++类与对象

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章 &#x1f525;座右铭&#xff1a;“不要等到什么都没有了&#xff0c;才下定决心去做” &#x1…...

Java并发编程与API详解

文章目录前言操作系统——进程和线程进程进程组成进程状态进程控制进程创建进程终止进程阻塞和唤醒进程通信线程线程组成线程状态线程控制线程的实现方式用户线程内核线程混合方式CPU调度调度的层次调度的实现调度器调度的时机、切换与过程进程调度的方式闲逛进程两种线程的调度…...

【冲刺蓝桥杯的最后30天】day5

大家好&#x1f603;&#xff0c;我是想要慢慢变得优秀的向阳&#x1f31e;同学&#x1f468;‍&#x1f4bb;&#xff0c;断更了整整一年&#xff0c;又开始恢复CSDN更新&#xff0c;从今天开始更新备战蓝桥30天系列&#xff0c;一共30天&#xff0c;如果对你有帮助或者正在备…...

大厂与小厂招人的区别,看完多少有点不敢相信

前两天在头条发了一条招人的感慨&#xff0c;关于大厂招人和小公司招人的区别。 大厂&#xff1a;有影响力&#xff0c;有钱&#xff0c;能够吸引了大量的应聘者。因此&#xff0c;也就有了筛选的资格&#xff0c;比如必须985名校毕业&#xff0c;必须35岁以下&#xff0c;不能…...

前端ES5对象特性

ES5对象特性 对象和函数的原型 JS中每一个对象都有一个特殊的内置属性&#xff0c;这个特殊的对象可以指向其他的对象 我们通过引用对象的属性key来获取一个value时&#xff0c;它会触发 Get 的操作首先检查该对象是否有对应的属性&#xff0c;如果有的话就使用对象内的如果…...

Linux入门介绍及Linux文件与目录结构

前言 本文小新为大家带来 Linux 入门介绍及Linux 文件与目录结构 相关知识&#xff0c;具体内容包括Linux入门介绍&#xff08;包括&#xff1a;Linux概述&#xff0c;Linux与Windows区别&#xff0c;CentOS 下载地址&#xff09;&#xff0c;Linux文件与目录结构等进行详尽介绍…...

超赞,用python实现流媒体服务器功能,寥寥几句搞定。

步骤&#xff1a; 要使用Python将实时摄像机传送流写入H5页面&#xff0c;可以使用以下步骤。 1、安装必要的软件包。您需要安装OpenCV和Flask以及gunicorn 与 gevent 。您可以通过在终端中运行以下命令来执行此操作。 pip install opencv-python pip install Flask pip ins…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...