Mybatis源码系列文章
手写源码(了解源码整体流程及重要组件)
Mybatis源码解析(一):环境搭建
Mybatis源码解析(二):全局配置文件的解析
Mybatis源码解析(三):映射配置文件的解析
Mybatis源码解析(四):sql语句及#{}、${}的解析
Mybatis源码解析(五):SqlSession会话的创建
Mybatis源码解析(六):缓存执行器操作流程
Mybatis源码解析(六):查询数据库主流程
Mybatis源码解析(七):Mapper代理原理
Mybatis源码解析(八):插件机制
什么是Mybatis插件?有什么作用?
注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {Signature[] value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {Class> type();String method();Class>[] args();
}
接口
public interface Interceptor {// 真正方法被拦截执行的逻辑Object intercept(Invocation invocation) throws Throwable;// 生成目标对象的代理对象default Object plugin(Object target) {return Plugin.wrap(target, this);}// 可以拦截器设置一些属性default void setProperties(Properties properties) {}
}
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class,Integer.class})
})
public class MyPlugin implements Interceptor {/*** 拦截方法:每次执行目标方法时,都会进入到intercept方法中* @param invocation :多个参数的封装类*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 增强逻辑:将执行的sql进行记录(打印)StatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();System.out.println("拦截方法,记录Sql:" + sql);return invocation.proceed();}/*** 将目标对象生成代理对象,添加到拦截器链中* @param target :目标对象*/@Overridepublic Object plugin(Object target) {// wrap 将目标对象,基于JDK动态代理生成代理对象return Plugin.wrap(target,this);}/*** 设置属性*/@Overridepublic void setProperties(Properties properties) {System.out.println("插件配置的初始化参数:" + properties);}
}
将插件注册到全局配置文件中
... ...
执行结果
已连接到目标 VM, 地址: ''127.0.0.1:51955',传输: '套接字''
插件配置的初始化参数:{username=zhangsan}
拦截方法,记录Sql:SELECT id,username FROM user WHERE id = ?
与目标 VM 断开连接, 地址为: ''127.0.0.1:51955',传输: '套接字''进程已结束,退出代码0
核心思想
进入
标签的解析方法
private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {// 获取拦截器String interceptor = child.getStringAttribute("interceptor");// 获取配置的Properties属性Properties properties = child.getChildrenAsProperties();// 根据配置文件中配置的插件类的全限定名 进行反射初始化Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();// 将属性添加到Intercepetor对象interceptorInstance.setProperties(properties);// 添加到配置类的InterceptorChain属性,InterceptorChain类维护了一个Listconfiguration.addInterceptor(interceptorInstance);}}
}
添加到拦截器链
public void addInterceptor(Interceptor interceptor) {interceptorChain.addInterceptor(interceptor);
}
public class InterceptorChain {private final List interceptors = new ArrayList<>();...public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}...
}
查看执行器对象的创建的源码
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : 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);}// 如果允许缓存,会通过CachingExecutor 去代理一层if (cacheEnabled) {executor = new CachingExecutor(executor);}// 拦截器插件executor = (Executor) interceptorChain.pluginAll(executor);return executor;
}
查看拦截器链的pluginAll方法
public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;
}
查看Plugin的wrap方法
public static Object wrap(Object target, Interceptor interceptor) {// 1.解析该拦截器所拦截的所有接口及对应拦截接口的方法Map, Set> signatureMap = getSignatureMap(interceptor);Class> type = target.getClass();// 2.获取目标对象实现的所有被拦截的接口Class>[] interfaces = getAllInterfaces(type, signatureMap);// 3.目标对象有实现被拦截的接口,生成代理对象并返回if (interfaces.length > 0) {// 通过JDK动态代理的方式实现return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}// 目标对象没有实现被拦截的接口,直接返回原对象return target;
}
查看Plugin的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 获取被拦截的方法 key:拦截的组件对象 value:拦截的组件对象的方法Set methods = signatureMap.get(method.getDeclaringClass());// 如果当前执行的方法属于拦截方法,那就执行代理对象的方法interceptif (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);}
}
查看自定义插件intercept方法的invocation.proceed()
public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);
}