mybatis学习笔记(十一)- 插件开发

1. 插件原理简介

  • MyBatis 在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。
    • MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。
    • 默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
      • Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
      • ParameterHandler (getParameterObject, setParameters)
      • ResultSetHandler (handleResultSets, handleOutputParameters)
      • StatementHandler (prepare, parameterize, batch, update, query)
  • 插件原理
    • 在四大对象创建的时候
      1. 每个创建出来的对象不是直接返回的,而是 interceptorChain.pluginAll(parameterHandler);
      2. 获取到所有的 Interceptor(拦截器)(插件需要实现的接口);调用 interceptor.plugin(target);返回target包装后的对象
      3. 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
        • 我们的插件可以为四大对象创建出代理对象;
        • 代理对象就可以拦截到四大对象的每一个执行;
	public Object pluginAll(Object target) {
		    for (Interceptor interceptor : interceptors) {
		      target = interceptor.plugin(target);
		    }
		    return target;
		  }

2. 插件的编写以及应用

1. 编写 Interceptor 的实现类,并且使用 @Intercepts 注解完成插件签名


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

	/**
	 * intercept:拦截:
	 * 		拦截目标对象的目标方法的执行;
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
		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;
	}

	/**
	 * plugin:
	 * 		包装目标对象的:包装:为目标对象创建一个代理对象
	 */
	@Override
	public Object plugin(Object target) {
		// TODO Auto-generated method stub
		//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
		System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}

	/**
	 * setProperties:
	 * 		将插件注册时 的property属性设置进来
	 */
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		System.out.println("插件配置的信息:"+properties);
	}

}

2. 将写好的插件注册到全局配置文件中

<!--plugins:注册插件  -->
	<plugins>
		<plugin interceptor="www.xq.mybatis.dao.MyFirstPlugin">
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
	</plugins>

3. 运行截图

mybatis学习笔记(十一)- 插件开发

3. 总结

  • 插件开发
    1. 编写插件实现Interceptor接口,并使用 @Intercepts 注解完成插件签名
      mybatis学习笔记(十一)- 插件开发
    2. 在全局配置文件中注册插件
      mybatis学习笔记(十一)- 插件开发
  • 插件原理
    1. 按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理
    2. 多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
    3. 目标方法执行时依次从外到内执行插件的intercept方法。
      mybatis学习笔记(十一)- 插件开发
  1. 多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
  • Interceptor 接口
    • Intercept:拦截目标方法执行
    • plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法
    • setProperties:注入插件配置时设置的属性
      mybatis学习笔记(十一)- 插件开发