Mybatis支持哪些插件類型厅缺?面試中80%人答不全

本文來自網(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)~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伴澄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子举农,更是在濱河造成了極大的恐慌航背,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件最盅,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)激挪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門薄湿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事桥嗤。” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長员辩。 經(jīng)常有香客問我妒穴,道長杰赛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮含友,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好概疆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布使套。 她就那樣靜靜地躺著,像睡著了一般计螺。 火紅的嫁衣襯著肌膚如雪馁痴。 梳的紋絲不亂的頭發(fā)上济欢,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音呐萨,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔚叨。 我是一名探鬼主播肤粱,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了府适?” 一聲冷哼從身側(cè)響起羔飞,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檐春,沒想到半個月后逻淌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疟暖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年擎鸠,在試婚紗的時候發(fā)現(xiàn)自己被綠了赎线。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情物蝙,我是刑警寧澤炎滞,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诬乞,受9級特大地震影響册赛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜震嫉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一森瘪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧票堵,春花似錦扼睬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至特纤,卻和暖如春军俊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捧存。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工粪躬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留担败,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓镰官,卻偏偏與公主長得像提前,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子泳唠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353