MyBatis插件原理-源码解读
来源:互联网 发布:redis查询所有数据库 编辑:程序博客网 时间:2024/06/07 02:29
MyBatis支持配置多个插件动态添加新的功能,因为存在InterceptorChain,很多人认为Mybatis采用责任链模式,看了源码后我觉得更像是装饰器模式。
Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截。下面依旧以StatementHandler类型的SQLStatsInterceptor为例:
package com.cuisea.mybatis;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.plugin.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.sql.Connection;import java.util.Properties;/** * Created by cuisea on 2017/7/18. */@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) })public class SQLStatsInterceptor implements Interceptor{ private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); logger.info("mybatis intercept sql:{}", sql); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { String dialect = properties.getProperty("dialect"); logger.info("mybatis intercept dialect:{}", dialect); }}MyBatis启动是会创建StatementHandler示例,见org.apache.ibatis.session.Configuration中的代码
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); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }interceptorChain.pluginAll(statementHandler);即将配置的plugin应用到statementHandler上。InterceptorChain代码:
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList(); public InterceptorChain() { } public Object pluginAll(Object target) { Interceptor interceptor; for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) { interceptor = (Interceptor)var2.next(); } return target; } public void addInterceptor(Interceptor interceptor) { this.interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(this.interceptors); }}可见,pluginAll方法实际上循环调用了interceptor的plugin方法,改方法中只有一句代码Plugin.wrap(target, this); Plugin关键代码如下:
public class Plugin implements InvocationHandler { private Object target; private Interceptor interceptor; private Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } 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; }}plugin的wrap方法通过自定义拦截器上的@Signature注解的type属性判断是否要对statementHandler进行拦截,如果需要拦截则生成JDK动态代理,否则返回原生的statementHandler对象。所以,如果针对statementHandler配置多个拦截器的话会进行层层wrap。
由于Plugin实现了InvocationHandler接口,对statementHandler代理对象调用时会调用Plugin的invoke方法:
@Override public 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); } }invoke方法会创建一个Invocation对象包装target
public class Invocation { private Object target; private Method method; private Object[] args; public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } public Object getTarget() { return target; } public Method getMethod() { return method; } public Object[] getArgs() { return args; } public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); }}自定义拦截器的intercept最后调用invocation.proceed()指向下一个拦截器或者最终的StatementHandler。
阅读全文
0 0
- MyBatis插件原理-源码解读
- 【MyBatis源码分析】插件实现原理
- Mybatis源码解读
- mybatis源码解读(1)
- mybatis源码解读(2)
- mybatis源码解读(3)
- mybatis源码解读(5)
- mybatis源码解读(6)
- mybatis源码解读(7)
- mybatis源码解读(8)
- mybatis之MapperFactoryBean源码解读
- 【Mybatis】mybatis插件源码分析
- 深入浅出Mybatis-插件原理
- 深入浅出Mybatis-插件原理
- MyBatis插件的原理
- 深入浅出Mybatis-插件原理
- 深入浅出Mybatis-插件原理
- mybatis插件原理
- 十种排序算法介绍(上)zz
- React Native通信机制详解
- 十种排序算法介绍(中)zz
- 十种排序算法介绍(下) zz
- 培养创造性思维的20个技巧[转]
- MyBatis插件原理-源码解读
- log4j.properties配置学习
- 计算1-1/2+1/3-....1/100
- 结束多重循环
- python中的多态和鸭子模型
- js 数组添加 删除
- Keras资料
- ZooKeeper环境搭建
- Linux环境中配置安装JDK tar.gz