Mybatis插件
创始人
2024-05-30 15:58:09
0

插件使用

动手实现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注入,目前比较少用了



拦截器原理

public class InterceptorChain {private final List 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 getInterceptors() {return Collections.unmodifiableList(interceptors);}}

拦截器的添加

初始化的添加

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

public class InterceptorChain {private final List 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 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, Set> 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 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);
}

相关内容

热门资讯

雅鲁藏布大峡谷的导游词 雅鲁藏布大峡谷的导游词范文(通用12篇)  导游词是导游人员引导游客观光游览时的讲解词,是导游员同游...
广西著名德天瀑布导游词 广西著名德天瀑布导游词  作为一名专门为游客提供帮助的导游,可能需要进行导游词编写工作,导游词是导游...
华山导游词 华山导游词范文  导游词范文一  朋友们:大家好!  欢迎大家来华山观光旅游!今天由我给大家做导游服...
广东顺德清晖园概况讲解词 广东顺德清晖园概况讲解词  作为一位出色的导游人员,就难以避免地要准备导游词,导游词是导游员在游览时...
贵州兴义万峰湖导游词 贵州兴义万峰湖导游词  万峰湖位于贵州省黔西南自治州首府兴义市东南部,是中华人民共和国兴义国家地质公...
扬州个园简介导游词 扬州个园简介导游词  导语:扬州的个园,是一座独具风格的名园。它是清嘉庆、道光年间兴建起来的。当时园...
千岛湖导游词 千岛湖导游词  作为一名导游,有必要进行细致的导游词准备工作,导游词是我们引导游览时使用的讲解词。那...
四川峨眉山导游词 四川峨眉山导游词15篇  作为一名优秀的旅游从业人员,通常会被要求编写导游词,导游词具有极强的实用性...
四川经典导游词 四川经典导游词(通用13篇)  作为一位无私奉献的导游,通常需要用到导游词来辅助讲解,导游词是导游员...
鹿邑老君台导游词 鹿邑老君台导游词  老君台原名升仙台或拜仙台,原为明道宫的一部分,位于老子故里鹿邑县城内东北隅,老君...
长城英文导游词最新 长城英文导游词【最新】  长城是我国著名的古建筑,下面是由应届毕业生小编为大家带来的关于长城英文导游...
西部梦幻峡谷旅游区导游词 西部梦幻峡谷旅游区导游词各位游客,女士们、先生们:  大家上午好!我叫巴图,蒙古语意为坚固的、可靠的...
颐和园英文导游词 颐和园英文导游词精选  Hello, welcome you to Beijing Summer P...
黄山云海导游词 黄山云海导游词范文(精选10篇)  作为一名专门引导游客、助人为乐的导游,总归要编写导游词,导游词可...
宜春明月山导游词 宜春明月山导游词  宜春是个出美景的好地方。小编下面为大家收集整理了宜春明月山导游词,欢迎阅读!  ...
河南导游词 河南导游词  作为一位出色的导游人员,常常要写一份好的导游词,导游词具有极强的实用性,涉及的知识十分...
民居导游词六百字 民居,也是特色的景点之一。以下是PINCAI小编整理的关于导游词的相关内容,欢迎阅读和参考!民居导游...
英文导游词欢迎词 篇一:英文导游欢迎词范文Ladies and gentlemen:Welcome to ______...
介绍青岛的导游词 介绍青岛的导游词  作为一名专门引导游客、助人为乐的导游,往往需要进行导游词编写工作,导游词的主要特...
介绍山西普救寺导游词 介绍山西普救寺导游词  作为一无名无私奉献的导游,时常要开展导游词准备工作,导游词是导游员在游览时为...