读mybatis源码之十三:拦截器

一、拦截器加载配置

在configuration里面可以配置plugin,他是在XMLConfigBuilder装配进去的
  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);
      }
    }
  }

二、哪些位置可以加拦截器

在configuration里面,有四处位置可以添加拦截器:
 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);

resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);

statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);

 executor = (Executor) interceptorChain.pluginAll(executor);

也就是说可以在参数、结果、声明、执行四处位置添加拦截器
可以看这里是拦截器链interceptorChain,所以可以同时加不同的拦截器

三、拦截器可以做哪些事情

动态修改参数(数据加密),动态修改结果集(数据解密)、动态修改声明处理(分表),动态修改执行器(去掉本地缓存参数)
例如,参数拦截器,参数加密;
@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = { PreparedStatement.class }) })
public class ParameterInterceptor implements Interceptor{
	private static final String tag = ParameterInterceptor.class.getName();
	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		ParameterHandler  parameterHandler = (ParameterHandler)invocation.getTarget();
		MetaObject metaParameterHandler= MetaObject.forObject(
				parameterHandler, DEFAULT_OBJECT_FACTORY,
				DEFAULT_OBJECT_WRAPPER_FACTORY);
		Object parameterObj =metaParameterHandler
				.getValue("parameterObject");
		BoundSql boundSql = (BoundSql) metaParameterHandler
				.getValue("boundSql");
		MappedStatement mappedStatement = (MappedStatement) metaParameterHandler
				.getValue("mappedStatement");
		String id = mappedStatement.getId();
		String className = id.substring(0, id.lastIndexOf("."));
		Class<?>  classObj = Class.forName(className);
		//根据配置加密
		TableSeg tableSeg = classObj.getAnnotation(TableSeg.class);
		if(tableSeg!=null){
			String[] fields = tableSeg.encryptFields();
			if(fields.length>0){
				EncryptParameter  as = new EncryptParameterImpl(mappedStatement, parameterObj, boundSql);
				as.encryptParamter(fields);
			}
		}
		//继续执行
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		// 当目标类是ParameterHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的
		// 次数
		if (target instanceof ParameterHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		
	}

}



四、拦截器原理

executor = (Executor) interceptorChain.pluginAll(executor);

 public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
可以看到返回的是插件包装后的对象,所有的拦截都会包装,而拦截器plugin由具体拦截器实现,拦截器都需要实现Interceptor接口:
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties); 
}
plugin用于包装,因为所有的拦截器都会包装下,所以这个方法里面需要针对性的过滤下,什么拦截器就包装什么,比如ExecutorInterceptor拦截器:
@Override
 public Object plugin(Object target) {
  // 当目标类是Executor类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的
  // 次数
  if (target instanceof Executor) {
   return Plugin.wrap(target, this);
  } else {
   return target;
  }
 }
可以看到只有是Executor的时候才返回包装对象,其他的不管。
具体看Plugin.wrap(target, this)将对象怎么包装了。
  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));
    }
    return target;
  }
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);     
//获取拦截器拦截参数配置,也就是注解参数,例如:
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,RowBounds.class,ResultHandler.class}) })
表示类型Executor,拦截方法query,参数类型
最后实际上他返回的是一个动态代理对象。没有接口的直接返回对象, 而执行方法调用的时候,调用的是代理的invoke方法,也就是plugin的invoke方法
  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);
    }
  }
判断配置的方法是不是执行调用方法,如果是执行拦截器 intercept方法,不是直接执行方法调用
拦截器的 intercept方法也就是我们定义拦截器,自定义处理的方法。自定义方法可以通过 Invocation获取参数
在自定义方法中做完自己的事情最后需要执行,
 //继续执行
return invocation.proceed();
而这个方法其实就是方法调用,也就是说动态代理就是提供了 自定义方法调用的一个时机。
return method.invoke(target, args);







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值