MyBatis工作原理源码分析(五)——插件机制

一、引言

从前几篇对Mybatis源码的分析知道,Mybatis的四大对象:Executor、StatementHandler、ParameterHandler、ResultSetHandler在创建的时候,每个对象都不是直接返回的,而是用interceptorChain.pluginAll();方法进行了包装,返回的是包装后的代理对象。代码如下(此处只贴出Executor的包装代码):

//Executor创建时候的代码
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);
   }
   if (cacheEnabled) {
     executor = new CachingExecutor(executor);
   }
   //用拦截器将Executor包装一下返回
   executor = (Executor) interceptorChain.pluginAll(executor);
   return executor;
 }

二、插件机制原理

1、原理分析

(1)、在四大对象创建的时候,每个创建出来的对象不是直接返回的,而是用InterceptorChain的pluginAll();方法进行包装。

public Object pluginAll(Object target) {
	  for (Interceptor interceptor : interceptors) {
	    target = interceptor.plugin(target);
	  }
	  return target;
}

(2)、从上一步可以看出,会获取到所有的Interceptor(拦截器,插件需要实现的接口),用所有的 Interceptor调用各自 plugin(target); 对目标对象target进行包装。

2、插件机制

插件机制就是利用上面类似于拦截器的原理,我们可以使用插件为目标对象创建一个代理对象(类似于Spring中的面向切面AOP),我们的插件(plugin)可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象执行的每一个前后。

3、插件方法的说明

public class MyFirstPlugin implements Interceptor{

	/**
	 * intercept()方法:
	 * 		拦截目标对象的目标方法的执行;其中invocation.proceed();表示执行目标方法
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {	
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	/**
	 * plugin()方法:
	 * 		包装目标对象:为目标对象创建一个代理对象
	 */
	@Override
	public Object plugin(Object target) {
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}

	/**
	 * setProperties()方法:
	 * 		将插件在全局配置文件中注册时,设置的 property 属性设置进来
	 */
	@Override
	public void setProperties(Properties properties) {
		System.out.println("插件配置的信息:"+properties);
	}

}

三、自定义一个插件

1、编写Interceptor的实现类

public class MyFirstPlugin implements Interceptor{

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询1号员工
		Object target = invocation.getTarget();
		System.out.println("当前拦截到的对象:"+target);
		//拿到:StatementHandler==>ParameterHandler===>parameterObject
		//拿到target的元数据
		MetaObject metaObject = SystemMetaObject.forObject(target);
		Object value = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println("sql语句用的参数是:"+value);
		//修改完sql语句要用的参数
		metaObject.setValue("parameterHandler.parameterObject", 11);
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	@Override
	public Object plugin(Object target) {
		//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
		System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}
	
	@Override
	public void setProperties(Properties properties) {
		System.out.println("插件配置的信息:"+properties);
	}

}

2、使用@Intercepts注解完成插件签名

/**
 * 完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts(
{
	@Signature(type=StatementHandler.class,
		method="parameterize",
		args=java.sql.Statement.class)
})

表示拦截 StatementHandler 的 parameterize() 方法;

3、将写好的插件注册到全局配置文件中


<plugins>
	<plugin interceptor="com.xudong.mybatis.test1.MyFirstPlugin">
		<property name="username" value="root"/>
		<property name="password" value="123456"/>
	plugin>
plugins>

4、运行结果

MyBatis工作原理源码分析(五)——插件机制_第1张图片
MyBatis工作原理源码分析(五)——插件机制_第2张图片

四、小结

1、插件编写步骤:

(1)、编写Interceptor的实现类
(2)、使用@Intercepts注解完成插件签名
(3)、将写好的插件注册到全局配置文件中

2、如果自定义两个插件,结果又是什么?

自定义第二个插件步骤同上,下面看一下运行结果。
MyBatis工作原理源码分析(五)——插件机制_第3张图片
此处需要注意的是包装步骤,用MyFirstPlugin包装完,再把包装后的结果给MySecondPlugin包装,所以调用顺序就得是反着的。
MyBatis工作原理源码分析(五)——插件机制_第4张图片

说白了,这就是代理模式的使用,为每个对象都创建一个代理,然后用代理对象去调用目标对象的方法,这样可以很好的控制目标方法。Spring中的拦截器和AOP都是基于此种模式

你可能感兴趣的:(Mybatis)