Mybatis插件的加载原理
1.MyBatis支持自定义插件,目的是能让开发者结合业务对框架进行增强,自定义的插件类必须实现org.apache.ibatis.plugin.Interceptor接口。切需要在核心XML配置文件中通过plugins显式声明,如:
2.进入XMLConfigBuilder类的pluginElement()方法,这里式解析核心配置文件plugins标签的地方,根据源码得知这里解析plugin标签根据反射生成插件实现类并保存到configuration的interceptorChain中一个Interceptor的集合。
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
this.configuration.addInterceptor(interceptorInstance);
}
}
}
总结:根据这里可以得知如果多个插件同时增强代理一个Handler那么在配置文件plugins标签中排在最后的插件拦截器最先执行。
Mybatis插件依靠动态代理生成代理类原理
用Execute举例
1.SqlSessionFactory的openSession()方法中openSessionFromDataSource()方法中会创建Execute的实现类并赋值给SqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 在此处实力Execute
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
2.进入configuration.newExecutor(tx, execType);方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
// 根据XML中配置的拦截器,为现有的Executor 进行动态代理增强
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
3.进入InterceptorChain的pluginAll()方法
interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调用拦截器链中的拦截器依次的对目标进行拦截或增强。interceptor.plugin(target)中的target就可以理解为原生的Executor 。返回的target是被重重代理后的对象
// 注册的拦截器插件集合
private final List<Interceptor> interceptors = new ArrayList();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
4.进入每个插件拦截器的interceptor.plugin(target);方法
拦截器Interceptor有三个抽象方法需要实现分别是
public interface Interceptor {
//当代理Executor执行被拦截的方法是,对应的拦截器会监听到并执行intercept方法参数Invocation 包含
//private final Object target; 被代理的Executor或者Executor的上一层代理类
//private final Method method;被代理的Executor所执行的方法
//private final Object[] args;执行方法的参数
//三大对象,一般实现会:{
//sysout("对方法进行了前置增强....");
//invocation.proceed(); 执行原方法
//sysout("对方法进行了后置增强....");
//}
Object intercept(Invocation var1) throws Throwable;
// 主要是为了把这个拦截器生成一个代理放到拦截器链中
//参数为Executor对象,返回值为Executor代理对象
//InterceptorChain的pluginAll()方法中调用用于生成Executor的重重代理对象
// 一般实现{return Plugin.wrap(target,this);}
Object plugin(Object var1);
//插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来即获取plugin标签中的property标签内容
void setProperties(Properties var1);
}
5.进入Plugin.wrap(target,this);方法
public static Object wrap(Object target, Interceptor interceptor) {
// 获取插件拦截器拦截的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获取代理前对象的类型
Class<?> type = target.getClass();
// 获取代理前对象所实现的全部接口
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
// 如果所实现的接口大于0则使用JDK动态代理生成代理类,如果小于0则返回原对象。
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
总结:
在创建Executor的实现类的过程中如果有插件拦截器拦截可Executor中的某个方法并且有再XML中显式配置则生成的Executor实现类为动态代理对象。
Mybatis代理类执行原理
用Execute中的query方法举例
1.根据Mybatis插件依靠动态代理生成代理类原理可以如果Execute为代理类,当query方法执行时,实际上执行的应该时Plugin中的invoke方法,因为Execute为JDK动态代理产生的代理类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try
{
/**获取被拦截方法列表,比如: * signatureMap.get(Executor.class), 可能返回 [query, update, commit] */
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){
}
}
总结:插件的实现原理采用了代理模式(JDK动态代理)