Mybatis源码分析(六)插件的建立代理过程

1、配置

MyBatis 容许你在已映射语句执行过程当中的某一点进行拦截调用。默认状况下,MyBatis 容许使用插件来拦截的方法调用包括:Executor、ParameterHandler 、ResultSetHandler 、StatementHandlersql

这几个方法咱们在开篇第一章节就已经介绍了,分别是执行器、参数处理器、返回结果集处理器、Statement处理器。 一般,咱们在xml文件中经过plugins属性来定义它们。数据库

<property name="plugins">
	<array>
		<bean class="com.viewscenes.netsupervisor.interceptor.ExecutorIntercepor"></bean>
		<bean class="com.viewscenes.netsupervisor.interceptor.ResultSetInterceptor"></bean>
		<bean class="com.viewscenes.netsupervisor.interceptor.PageInterceptor"></bean>
	</array>
</property>
复制代码

那么,在构建SqlSessionFactory的时候,Mybatis就会检查是否配置了插件。有的话,也比较简单,就是加入到interceptors集合中。bash

if (!isEmpty(this.plugins)) {
	for (Interceptor plugin : this.plugins) {
		configuration.addInterceptor(plugin);
	}
}
public class InterceptorChain {
	public void addInterceptor(Interceptor interceptor) {
		interceptors.add(interceptor);
	}
}
复制代码

而后新建一个类,实现Interceptor接口,同时经过@Intercepts声明接口的名称、方法名称、参数列表便可实现插件。好比下面的例子中,声明了拦截的接口为Executor,方法名为query,参数为args。app

@Intercepts({@Signature(type = Executor.class, method = "query", args = {
		MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class ExecutorIntercepor implements Interceptor{
	
	public Object intercept(Invocation invocation) throws Throwable {
		return invocation.proceed();
	}
	public Object plugin(Object target) {
		if (target instanceof Executor){
			return Plugin.wrap(target, this);
		}
		return target;
	}
	public void setProperties(Properties properties) {}
}
复制代码

2、建立代理

所谓插件,其实就是建立代理的过程。就上面的例子而言,就是建立了Executor接口的代理类,调用程序处理器为Plugin类。在执行Executor.query()方法的时候,实际调用的是Plugin.invoke(Invocation invocation)。 咱们说拦截的方法包含以上四种,那就一个一个来看,它们究竟是怎么实现拦截的。ui

一、Executor

在上一章节分析SQL的执行过程的时候咱们看到,Mybatis会先建立一个sqlSession对象。在建立sqlSession的时候,就会建立一个执行器。Executor.query方法是一开始就调用的方法,此时SQL还都是一个一个的sqlNode节点未解析的状态。this

public class Configuration {
	public Executor newExecutor(Transaction transaction, ExecutorType 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);
		}
		//默认为true,把SimpleExecutor包装成CachingExecutor对象
		if (cacheEnabled) {
			executor = new CachingExecutor(executor);
		}
		//产生代理的地方,若是配置了插件,最后返回的executor就是个代理对象
		executor = (Executor) interceptorChain.pluginAll(executor);
		return executor;
	}
}
复制代码

能够看到,Mybatis会根据类型建立一个执行器。而后调用interceptorChain.pluginAll(executor)来肯定是否须要产生代理。spa

public class InterceptorChain {
	public Object pluginAll(Object target) {
		for (Interceptor interceptor : interceptors) {
			target = interceptor.plugin(target);
		}
		return target;
	}
}
复制代码

interceptor咱们知道,在构建SqlSessionFactory的时候,就把配置的拦截器加入到其中了。那么在这里,它是循环全部自定义的拦截器,调用其plugin方法。这也就解释了咱们为何在plugin方法中要进行类型判断,不然每次返回的对象就是最后一个拦截器的代理对象。.net

public Object plugin(Object target) {
	if (target instanceof Executor) {
		return Plugin.wrap(target, this);
	}
	return target;
}
复制代码

若是,类型匹配上的话就调用Plugin的静态方法wrap,实际产生代理。也就是说,@Signature注解上配置的是哪一个接口,这里就产生哪一个接口的代理。插件

public class Plugin implements InvocationHandler {
	public static Object wrap(Object target, Interceptor interceptor) {
		//获取@Signature注解的接口,方法和参数
		Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
		Class<?> type = target.getClass();
		//获取目标类实现的接口
		Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
		if (interfaces.length > 0) {
			//调用JDK的方法,返回代理对象
			//调用程序处理器Plugin就是本类,它已经实现了InvocationHandler接口
			return Proxy.newProxyInstance(
			type.getClassLoader(),
			interfaces,
			new Plugin(target, interceptor, signatureMap));
		}
		return target;
	}
}
复制代码

那么,在调用到Executor.query()方法的时候,实际执行的是Plugin类的invoke()。在invoke方法里面就会判断当前调用的方法是否在自定义拦截器注解方法的范围内,而后调用其intercept方法。代理

public class Plugin implements InvocationHandler {
	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);
		}
	}
}
复制代码

二、StatementHandler

在执行到SimpleExecutor.doQuery方法的时候,要建立StatementStatementHandler对象,这里也能够配置拦截器。这时候,SQL语句已经解析完毕,开始要调用StatementHandler.prepare方法进行预编译SQL。思考一下,咱们能够拦截它作什么呢? 固然了,它们的建立过程都是同样的,都是调用interceptorChain.pluginAll(executor)

public class 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;
  }
}
复制代码

三、ResultSetHandler

在上一步实例化PreparedStatementHandler对象的时候,会调用其父类的构造方法,在这里建立了两个对象:ResultSetHandler、ParameterHandler。ResultSetHandler是返回值集合处理类,它的handleResultSets方法返回的就是转换完毕的Java数据集合。 能够思考下,在这里拦截的话,能够干些什么呢?

public abstract class BaseStatementHandler{
		
	protected final ResultSetHandler resultSetHandler;
	protected final ParameterHandler parameterHandler;
  
	protected BaseStatementHandler(Executor executor, 
			MappedStatement mappedStatement, Object parameterObject, 
			RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
		
		this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
		this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, 
										rowBounds, parameterHandler, resultHandler, boundSql);
	}
}
复制代码

它建立的是返回值集合处理器是DefaultResultSetHandler,同时同样的也会调用interceptorChain.pluginAll验证是否要产生代理。

public class Configuration {
	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);
		resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
		return resultSetHandler;
	}
}
复制代码

3、总结

本章节阐述了三种经常使用拦截器的配置方式和解析过程,分别是Executor、StatementHandler、ResultSetHandler,它们执行的时机分别以下:

Executor

生成sqlSession对象以后,开始调用Executor进行实际方法的调用。

StatementHandler

解析完SQL,建立PreparedStatement对象预编译并设置参数。

ResultSetHandler

从数据库拿到数据,并转换为Java数据集合以后返回。

思考一下,这三种类型拦截器咱们能够哪来作什么呢?下节课,笔者会拿实际案例来展现它们的应用。

相关文章
相关标签/搜索