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);
}

相关内容

热门资讯

二年级游乐园冲浪作文(优秀6... 二年级游乐园冲浪作文 篇一我最喜欢的游乐园冲浪今天,我和爸爸妈妈去了一个很大很有趣的游乐园。这个游乐...
二年级有趣的游戏作文200字... 二年级有趣的游戏作文200字 篇一:捉迷藏捉迷藏是我们二年级最喜欢的游戏之一。这个游戏的规则很简单:...
我的好朋友二年级作文【实用6... 我的好朋友二年级作文 篇一我的好朋友我有一个非常好的朋友,她叫小芳。她和我在同一个班级,我们从小学一...
我的家【优质6篇】 我的家 篇一我的家是一个温馨而快乐的地方。无论是平日的热闹还是周末的宁静,家里总是充满着欢声笑语和爱...
二年级作文不少于200个字【... 二年级作文不少于600个字 篇一我的暑假生活暑假终于来了,我迫不及待地迎接了这个长假。在这个暑假里,...
二年级暑假趣事作文捉老鼠(推... 二年级暑假趣事作文捉老鼠 篇一暑假快到了,我和弟弟决定在家里玩捉老鼠的游戏。我们找来了一些小道具,准...
小学二年级避暑山庄旅游作文(... 小学二年级避暑山庄旅游作文 篇一我和家人去了一个非常有趣的地方——避暑山庄。这个地方真的很美,有很多...
二年级作文游庐山【精简6篇】 二年级作文游庐山 篇一我和爸爸妈妈一起去了庐山。庐山是中国著名的山岳风景区,被誉为“江南第一山”。我...
二年级下册看图写话春天来了作... 二年级下册看图写话春天来了作文 篇一春天来了春天来了,大地变得生机勃勃。图中的小朋友们正在户外玩耍,...
二年级打雪仗作文指导【通用6... 二年级打雪仗作文指导 篇一打雪仗是冬天最有趣的活动之一,对于二年级的小朋友来说,更是一种享受。下面是...
二年级美丽的早晨作文(实用6... 二年级美丽的早晨作文 篇一美丽的早晨早晨的阳光透过窗户洒进来,房间里弥漫着一股清新的味道。我慢慢睁开...
小学二年级海边旅游作文200... 小学二年级海边旅游作文200字作文 篇一我和家人去海边旅游了,真是一个美好的经历!早上,我们一大早就...
二年级【优秀6篇】 二年级 篇一:我的暑假生活暑假终于来了,我迫不及待地开始了我的暑假生活。在这个悠长的假期里,我过得非...
赏荷花二年级作文【通用6篇】 赏荷花二年级作文 篇一欣赏荷花的美丽今天,我和爸爸妈妈一起去公园赏荷花。公园里有一个大大的荷花池,里...
舞蹈汇演作文二年级【精彩6篇... 舞蹈汇演作文二年级 篇一舞蹈汇演是一场精彩绝伦的表演,让我感受到了舞蹈的魅力和美妙。我在二年级的时候...
二年级作文我家的厨师(优质6... 二年级作文我家的厨师 篇一我家的厨师是我妈妈。她是一个非常厉害的厨师,每天都能给我们做出美味可口的饭...
童年趣事作文:枕头大战【精选... 童年趣事作文:枕头大战 篇一小时候的我总是充满了无尽的精力和好奇心,每天都在探索世界的各个角落。而最...
家乡的菊花作文二年级(经典6... 家乡的菊花作文二年级 篇一家乡的菊花我家乡是一个美丽的小镇,四季如春,花草繁盛。其中,最引人注目的要...
二年级小作文28篇【精简3篇... 二年级小作文28篇 篇一我最喜欢的动物我最喜欢的动物是猫。猫咪有软软的毛,尤其是它们的小脸上,摸起来...
二年级写我的家乡【优选6篇】 二年级写我的家乡 篇一我的家乡是一个美丽的小城镇。它位于一个宽广的山谷之中,四周环绕着青翠欲滴的群山...