讀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);







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章