MyBatis源码插件Plugin学习

查看MyBatis官方文档发现,MyBatis允许插件拦截调用的方法包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)
    mybatis的plugin包中的类
    mybatis中有一个plugin包,包下面有上面几个类。在这个包里面,自定义了一个异常类PluginException继承的RuntimeException运行时异常。Plugin类implements InvocationHandler。

InterceptorChain这个类中有个属性List interceptors存放的是配置文件中每一个对应的plugin。而InterceptorChain中的pluginAll(Object target)就是实现AOP扩展功能的方法媒介。只要调用了这个方法返回的对象,就说名这个类是可以被拦截的。通过搜索方法调用发现刚好和文档的四个类对应上。
在这里插入图片描述

//InterceptorChain,实现AOP扩展功能
public Object pluginAll(Object target) {
	for (Interceptor interceptor : interceptors) {
    	target = interceptor.plugin(target);
    }
    return target;
}
//Interceptor接口默认的方法
default Object plugin(Object target) {
    return Plugin.wrap(target, this);
}
//Plugin
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,
          	new Plugin(target, interceptor, signatureMap));//:Plugin实现InvocationHandler接口
    }
    return target;
}
//代理对象
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)) {
        	//注释的方法不为空,而且包含类调用的方法
            //看拦截器怎么具体实现的intercept方法
        	return interceptor.intercept(new Invocation(target, method, args));
      	}
      	//没有拦截器就正常调用
      	return method.invoke(target, args);
    } catch (Exception e) {
      	throw ExceptionUtil.unwrapThrowable(e);
    }
}

读取配置文件,将plugin的类插入InterceptorChain的属性list集合中

//XMLConfigBuilder解析pluginsxml文档的代码
private void pluginElement(XNode parent) throws Exception {
	if (parent != null) {
    	for (XNode child : parent.getChildren()) {
        	String interceptor = child.getStringAttribute("interceptor");
        	Properties properties = child.getChildrenAsProperties();
            //resolveClass(interceptor)这个没有配置别名在注册中心,通过全名进行类加载获得class,通过反射创建对象
        	Interceptor interceptorInstance = 					(Interceptor)resolveClass(interceptor).getDeclaredConstructor().newInstance();
            //plugin节点下的key、value键值对转换成Properties对象赋值给Interceptor对象
			interceptorInstance.setProperties(properties);
            //configuration对象中的InterceptorChain对象添加Interceptor拦截器对象
        	configuration.addInterceptor(interceptorInstance);
		}
	}
}

> plugin简单实现example
~~~java
//插件实现Interceptor接口
@Intercepts(@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}))
public class MyPlugin implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //方法调用之前处理
        return invocation.proceed();
    }
}
<!--mybatis-config.xml-->
<plugins>
	<plugin interceptor="com.study.plugin.MyPlugin">
    	<property name="" value=""/>
	</plugin>
</plugins>
//最终一个简单查询语句会执行到executor.query,要想实现拦截,executor对象的创建必须要实现动态代理扩展,AOP
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值