8-mybatis深入学习-源码学习之mybaits插件开发

一、mybatis支持插件开发

插件是用来改变或者扩展mybatis的原有的功能,mybaits的插件就是通过继承Interceptor拦截器实现的;
mybatis中能使用插件进行拦截的接口和方法如下:
Executor(update、query 、 flushStatment 、 commit 、 rollback 、 getTransaction 、 close 、 isClose)
StatementHandler(prepare 、 paramterize 、 batch 、 update 、 query)
ParameterHandler( getParameterObject 、 setParameters )
ResultSetHandler( handleResultSets 、 handleCursorResultSets 、 handleOutputParameters )

二、mybatis插件实现

1、实现插件

MyBatis 插件的实现必须实现 Interceptor 接口, 该接口有如下三个方法

  1. org.apache.ibatis.plugin.Interceptor.intercept(Invocation):插件对业务进行增强的核心方法;
  2. org.apache.ibatis.plugin.Interceptor.plugin(Object):target
    是被拦截的对象,它的作用就 是给被拦截的对象生成一个代理对象;
  3. org.apache.ibatis.plugin.Interceptor.setProperties(Properties):读取在
    plugin 中设置的参数;
@Intercepts({
//拦截StatementHandler类的query方法
	@Signature(type=StatementHandler.class,method="query",args={Statement.class, ResultHandler.class})
//	@Signature(type=StatementHandler.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class testInterceptor implements Interceptor {
	private long threshold;
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		long begin = System.currentTimeMillis();
		Object ret = invocation.proceed();
	    //处理逻辑
		return ret;
	}
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	@Override
	public void setProperties(Properties properties) {
		this.threshold = Long.valueOf(properties.getProperty("threshold"));
	}
}

2、mybatis.config.xml文件中引入插件

<plugins>
		<plugin interceptor="com.mybatis.Interceptors.testInterceptor"> 
			<property name="threshold" value="10"/>
		</plugin>
</plugins>

三、mybatis插件实现源码分析

1、插件注入

在解析这核心配置文件mybatis-config.xml文件时就会解析这个插件节点,解析后,会将这些类加到configuration的InterceptorChain 对象中

 pluginElement(root.evalNode("plugins"));

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

2、先找到BaseExecutor类的doQuery方法

@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

3、查看configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);这个方法

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);//加入所有插件实际上就是创建statementHandler 的代理对象
    return statementHandler;
  }

4、查看interceptorChain.pluginAll方法

public Object pluginAll(Object target) {
    //遍历publins里面所有我们定义的interceptors,这里只定了一个ThresholdInterceptor类,所以查看这个ThresholdInterceptor的plugin方法
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

5、interceptor实现类的pulugin方法

@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	//Plugin的wrap方法:
public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          //实现类,这个实现类的invoke()方法又调用interceptor的interceptor方法,所以最后调的就是我们实现的拦截类的interceptor方法
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

new plugin()对象的invoke()方法:

@Override
  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);
    }
  }

所以在调用stmt = handler.prepare(connection, transaction.getTimeout());这个方法时,会调用插件的invoke方法。
总结: 实际上插件也是通过动态代理实现的,在有插件的地方判断这个对象是否需要增强,如果需要增强就返回增强的代理类

四、mybaits支持插件采用了责任链模式

责任链模式: 就是把一件工作分别经过链上的各个节点,让这些节点依次处理这个工作;和装饰器模式不同,每个节点都知道后继者是谁;适合为完成同一个请求需要多个处理类的场景;
每个节点都知道后继者是谁,但现在一遍节点顺序都通过配置文件来定义,比如mybaits plugins的顺序是在mybatis-config.xml文件中定义的

<plugins>
		<plugin interceptor="com.mybatis.Interceptors.ThresholdInterceptor"> 
			<property name="threshold" value="10"/>
		</plugin>
  		 <plugin interceptor="com.test.mybatis.PageInterceptor">
			<property name="pageSizeZero" value="true" />
		</plugin>
</plugins>

要素分析
Handler:定义了一个处理请求的标准接口;
ConcreteHandler:具体的处理者,处理它负责的部分,根据业务可以结束处理流程,也可以将请求转发给它的后继者;
client :发送者,发起请求的客户端;
责任链模式优点:
降低耦合度。它将请求的发送者和接收者解耦。
简化了对象。使得对象不需要知道链的结构。
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
增加新的请求处理类很方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值