本文來自網(wǎng)易架構(gòu)師內(nèi)部分享湘捎,簡單介紹了Mybatis插件功能的使用和實(shí)現(xiàn)方式~
?
自己整理的Java架構(gòu)學(xué)習(xí)視頻和大廠項(xiàng)目底層知識點(diǎn),需要的同學(xué)歡迎私信我【資料】發(fā)給你~一起學(xué)習(xí)進(jìn)步娩践!
Mybatis支持哪些類型插件
Executor (update, query, flushStatements, commit, rollback,
getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
如何使用插件
自定義插件實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口
通過 @Intercepts注解表明插件增強(qiáng)的類型材泄,方法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class SQLStatsInterceptor implements Interceptor {
? ? private final Logger logger = LoggerFactory.getLogger(this.getClass());
? ? @Override
? ? public Object intercept(Invocation invocation) throws Throwable {
? ? ? ? StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
? ? ? ? BoundSql boundSql = statementHandler.getBoundSql();
? ? ? ? String sql = boundSql.getSql();
? ? ? ? System.out.println("mybatis intercept sql:" + sql);
? ? ? ? return invocation.proceed();
? ? }
? ? @Override
? ? public Object plugin(Object target) {
? ? ? ? return Plugin.wrap(target, this);
? ? }
? ? @Override
? ? public void setProperties(Properties properties) {
? ? ? ? String dialect = properties.getProperty("dialect");
? ? ? System.out.println("mybatis intercept dialect:"+dialect);
? ? }
}
config.xml中配置插件
<plugins>
????<plugin?interceptor="org.apache.ibatis.builder.ExamplePlugin">
??????<property?name="pluginProperty"?value="100"/>
????</plugin>
????<plugin?interceptor="org.apache.ibatis.builder.SQLStatsInterceptor">
??????<property?name="dialect"?value="test"/>
????</plugin>
??</plugins>
為什么只支持上述的四種類型的插件呢拉宗?
只是因?yàn)樵贑onfiguration中定義了如下幾個方法:
/**
? *plugin在此對ParameterHandler進(jìn)行增強(qiáng)
? */
? public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
? ? ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
? ? parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
? ? return parameterHandler;
? }
? /**
? *plugin在此對ResultSetHandler進(jìn)行增強(qiáng)
? */
? public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
? ? ? ResultHandler resultHandler, BoundSql boundSql) {
? ? ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
? ? resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
? ? return resultSetHandler;
? }
? /**
? *plugin在此對StatementHandler進(jìn)行增強(qiáng)
? */
? 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);
? ? return statementHandler;
? }
? public Executor newExecutor(Transaction transaction) {
? ? return newExecutor(transaction, defaultExecutorType);
? }
? /**
? *plugin在此對Executor進(jìn)行增強(qiáng)
? */
? public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
? ? executorType = executorType == null ? defaultExecutorType : executorType;
? ? executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
? ? Executor executor;
? ? if (ExecutorType.BATCH == executorType) {
? ? ? executor = new BatchExecutor(this, transaction);
? ? } else if (ExecutorType.REUSE == executorType) {
? ? ? executor = new ReuseExecutor(this, transaction);
? ? } else {
? ? ? executor = new SimpleExecutor(this, transaction);
? ? }
? ? if (cacheEnabled) {
? ? ? executor = new CachingExecutor(executor);
? ? }
? ? executor = (Executor) interceptorChain.pluginAll(executor);
? ? return executor;
? }
增強(qiáng)實(shí)現(xiàn)的源碼
在解析config.xml文件的時候會將<plugins>中配置的插件會添加到Configuration中到interceptorChain中
private void pluginElement(XNode parent) throws Exception {
? ? //如果存在plugings節(jié)點(diǎn)
? ? if (parent != null) {
? ? ? //遍歷所有子節(jié)點(diǎn)
? ? ? for (XNode child : parent.getChildren()) {
? ? ? ? //獲取interceptor屬性
? ? ? ? String interceptor = child.getStringAttribute("interceptor");
? ? ? ? Properties properties = child.getChildrenAsProperties();
? ? ? ? //創(chuàng)建Interceptor實(shí)例
? ? ? ? Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
? ? ? ? //設(shè)置屬性
? ? ? ? interceptorInstance.setProperties(properties);
? ? ? ? //向攔截器鏈注入攔截器
? ? ? ? configuration.addInterceptor(interceptorInstance);
? ? ? }
? ? }
? }
public void addInterceptor(Interceptor interceptor) {
? ? interceptorChain.addInterceptor(interceptor);
? }
SimpleExecutor中執(zhí)行doUpdate方法是會通過configuration去創(chuàng)建一個StatementHandler
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
? ? Statement stmt = null;
? ? try {
? ? ? log.trace(" SimpleExecutor ms.getConfiguration()");
? ? ? Configuration configuration = ms.getConfiguration();
? ? ? log.trace("SimpleExecutor configuration.newStatementHandler");
? ? ? StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
? ? ? stmt = prepareStatement(handler, ms.getStatementLog());
? ? ? return handler.update(stmt);
? ? } finally {
? ? ? closeStatement(stmt);
上文已經(jīng)列出來了族檬,在newStatementHandler中會對StatementHandler進(jìn)行增強(qiáng)
/**
? *plugin在此對StatementHandler進(jìn)行增強(qiáng)
? */
? 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);
? ? return statementHandler;
? }
/**
* 攔截器鏈
* @author Clinton Begin
*/
public class InterceptorChain {
? ? //攔截器列表
? ? private final List interceptors = new ArrayList();
? ? //對攔截器進(jìn)行增強(qiáng)
? ? public Object pluginAll(Object target) {
? ? ? ? //遍歷所有攔截器埋凯,逐個增強(qiáng)
? ? ? ? for (Interceptor interceptor : interceptors) {
? ? ? ? ? ? target = interceptor.plugin(target);
? ? ? ? }
? ? ? ? return target;
? ? }
? ? //添加攔截器
? ? public void addInterceptor(Interceptor interceptor) {
? ? ? ? interceptors.add(interceptor);
? ? }
? ? //獲取所有攔截器
? ? public List getInterceptors() {
? ? ? ? return Collections.unmodifiableList(interceptors);
? ? }
}
@Override
? ? public Object plugin(Object target) {
? ? ? ? return Plugin.wrap(target, this);
? ? }
通過上文中發(fā)現(xiàn)似乎所有攔截器都進(jìn)行了增強(qiáng)處理掠廓,事實(shí)上不是這樣都蟀瞧,具體使用哪些攔截器對接口進(jìn)行增強(qiáng)是如下方法中處理的。同時可以發(fā)現(xiàn) Plugin實(shí)現(xiàn)了InvocationHandler接口铸屉,也就是調(diào)用處理也是在該類中完成的彻坛,下面主要就是Plugin中的實(shí)現(xiàn)源碼
//對目標(biāo)對象使用過interceptor進(jìn)行增強(qiáng)
? public static Object wrap(Object target, Interceptor interceptor) {
? ? //獲取攔截類昌屉,方法映射表
? ? Map, Set> signatureMap = getSignatureMap(interceptor);
? ? //獲取目標(biāo)類類型
? ? Class type = target.getClass();
? ? //獲取目標(biāo)類所有需要攔截到接口
? ? Class[] interfaces = getAllInterfaces(type, signatureMap);
? ? //如果有攔截接口间驮,則創(chuàng)建代理對象對其進(jìn)行增強(qiáng)
? ? if (interfaces.length > 0) {
? ? ? //創(chuàng)建動態(tài)代理對象
? ? ? return Proxy.newProxyInstance(
? ? ? ? ? type.getClassLoader(),
? ? ? ? ? interfaces,
? ? ? ? ? new Plugin(target, interceptor, signatureMap));
? ? }
? ? return target;
? }
private static Map, Set> getSignatureMap(Interceptor interceptor) {
? ? //獲取類上@Intercepts的注解
? ? Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
? ? // 如果插件上沒有@Intercepts注解竞帽,拋出異常
? ? if (interceptsAnnotation == null) {
? ? ? throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());? ? ?
? ? }
? ? //@Intercepts注解中所有簽名
? ? Signature[] sigs = interceptsAnnotation.value();
? ? Map, Set> signatureMap = new HashMap, Set>();
? ? //遍歷所有簽名
? ? for (Signature sig : sigs) {
? ? ? //根據(jù)類型從簽名映射表中獲取方法集合
? ? ? Set methods = signatureMap.get(sig.type());
? ? ? if (methods == null) {
? ? ? ? //如果方法集合為null,則創(chuàng)建一個空集合并放入到映射表中
? ? ? ? methods = new HashSet();
? ? ? ? signatureMap.put(sig.type(), methods);
? ? ? }
? ? ? try {
? ? ? ? //根據(jù)方法名稱抢呆,參數(shù)類型列表從指定的class中獲取方法
? ? ? ? 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;
? }
這次可以發(fā)現(xiàn)通過JDK的動態(tài)代理進(jìn)行了增強(qiáng),在進(jìn)行方法調(diào)用對時候會執(zhí)行一下方法笛谦,在該方法中判斷當(dāng)前方法是否需要增強(qiáng)抱虐,如果需要就會調(diào)用interceptor 進(jìn)行增強(qiáng)處理
@Override
? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? try {
? ? ? Set methods = signatureMap.get(method.getDeclaringClass());
? ? ? //如果當(dāng)前方法需要被增強(qiáng)
? ? ? if (methods != null && methods.contains(method)) {
? ? ? ? //調(diào)用目標(biāo)對象對攔截器
? ? ? ? return interceptor.intercept(new Invocation(target, method, args));
? ? ? }
? ? ? //否則直接調(diào)用方法
? ? ? return method.invoke(target, args);
? ? } catch (Exception e) {
? ? ? throw ExceptionUtil.unwrapThrowable(e);
? ? }
? }
來源:網(wǎng)易工程師--張偉
有任何問題歡迎留言交流~
整理總結(jié)不易,如果覺得這篇文章有意思的話饥脑,歡迎轉(zhuǎn)發(fā)恳邀、收藏,給我一些鼓勵~
有想看的內(nèi)容或者建議灶轰,敬請留言谣沸!
最近利用空余時間整理了一些精選Java架構(gòu)學(xué)習(xí)視頻和大廠項(xiàng)目底層知識點(diǎn),需要的同學(xué)歡迎私信我發(fā)給你~一起學(xué)習(xí)進(jìn)步笋颤!有任何問題也歡迎交流~
Java日記本乳附,每日存檔超實(shí)用的技術(shù)干貨學(xué)習(xí)筆記,每天陪你前進(jìn)一點(diǎn)點(diǎn)~