源碼解讀-Mybatis 攔截器實現原理

package org.apache.ibatis.session;

public class Configuration {
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    // ...
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
	// ...
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // ...
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
    
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // ...
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

只有這四個方法調用了pluginAll(...)方法, 所以支持的可攔截的類型:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler

  • org.apache.ibatis.executor.Executor: 攔截執行器方法
    • 實現類: CachingExecutor
  • org.apache.ibatis.executor.statement.StatementHandler: 攔截SQL語法構建處理
    • 實現類: RoutingStatementHandler
  • org.apache.ibatis.executor.parameter.ParameterHandler: 攔截參數處理
    • 實現類: DefaultParameterHandler
  • org.apache.ibatis.executor.resultset.ResultSetHandler: 攔截結果集處理
    • 實現類: DefaultResultSetHandler

 

package org.apache.ibatis.plugin;

public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList<>();
    
    /**
     * 判斷當前 target 是否設置了攔截器
     * */
    public Object pluginAll(Object target) {
		for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
    }
}

 

@Intercepts(...)
public class XXXInterceptor implements Interceptor {
    // ...
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    // ...
}

參考: com.github.pagehelper.PageInterceptor

 

package org.apache.ibatis.plugin;

public class Plugin implements InvocationHandler {
    // ...
    
    /**
     * 包裝 目標對象(target) 爲 代理攔截器對象(interceptor)
     *
     * @param target
     *          目標對象, 實際對象
     * @param interceptor
     *          代理攔截器對象
     */
    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;
    }
    
    /**
     * 
     * */
    @Override
  	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Set<Method> methods = signatureMap.get(method.getDeclaringClass());
            // 判斷當前執行方法是否是自定義攔截器中的方法, 即 @Intercepts(@Signature(...)) 中籤名的方法
          	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);
        }
    }

    /**
     * 獲取 自定義攔截器 插件需要攔截的方法, 即註解 @Intercepts(...) 中配置的信息
     */
  	private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);

        if (interceptsAnnotation == null) {
            throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
		}
        Signature[] sigs = interceptsAnnotation.value();
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
        for (Signature sig : sigs) {
            Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
            try {
                Method method = sig.type().getMethod(sig.method(), sig.args());
            	methods.add(method);
          	} catch (NoSuchMethodException e) {
            	throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
          	}
		}
        return signatureMap;
      }

    // ...
}

 

註冊攔截器

InterceptorChain類中成員變量interceptors的值從哪來

mybatis.config-location=classpath:/mybatis/config/mybatis-config.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <plugins>
        <plugin interceptor="com.jaemon.oms.utils.PagePlugin">
            <property name="dialect" value="mysql" />
            <property name="pageSqlId" value="^(find).*(Page)$" />
            <property name="pageParam" value="pageInfo" />
        </plugin>
    </plugins>

</configuration>

 

package org.apache.ibatis.builder.xml;

public class XMLConfigBuilder extends BaseBuilder {
    // ...
    
    private void parseConfiguration(XNode root) {
        // ...
        pluginElement(root.evalNode("plugins"));
        // ...
    }
    
    
    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                // 獲取 plugin 標籤的 interceptor 屬性值
                String interceptor = child.getStringAttribute("interceptor");
                Properties properties = child.getChildrenAsProperties();
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                interceptorInstance.setProperties(properties);
                // 註冊攔截器
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章