当前位置: 首页 > 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);
}
http://www.lryc.cn/news/34404.html

相关文章:

  • 计算机学科专业基础综合科目(408)
  • centos7安装教程
  • Kafka 重平衡
  • PTA:L1-022 奇偶分家、L1-023 输出GPLT、L1-024 后天(C++)
  • IDEA插件开发入门.02
  • 如何用 23 种编程语言说“Hello World”
  • 【Linux快速入门】文件目录操作
  • 字体反爬慢慢总结破解方式
  • Kafka 位移提交
  • kubernetes--监控容器运行时:Falco
  • HTTP协议详解(上)
  • java性能-原生内存-内存分析
  • c++类与对象
  • Java并发编程与API详解
  • 【冲刺蓝桥杯的最后30天】day5
  • 大厂与小厂招人的区别,看完多少有点不敢相信
  • 前端ES5对象特性
  • Linux入门介绍及Linux文件与目录结构
  • 超赞,用python实现流媒体服务器功能,寥寥几句搞定。
  • 冥想第七百二十一天
  • 06-Oracle表空间与用户管理
  • Mysql 索引特点
  • 读书笔记-终身学习
  • 了解栈Stack一篇文章就够了
  • CNStack 助推龙源电力扛起“双碳”大旗
  • ruoyi-vue-plus1(控制台相关的输出日志)(p6spy插件)(jackson全局配置)(StopWatch)
  • 【Mybatis】| 如何创建MyBatis的工具类
  • 【Java】DT怎么写?
  • xcode14安装swift package设置github账户token
  • css面试题1