成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MyBatis插件原理分析,看完感覺(jué)自己better了

開(kāi)發(fā) 架構(gòu)
大多數(shù)框架都支持插件,用戶可通過(guò)編寫(xiě)插件來(lái)自行擴(kuò)展功能,Mybatis也不例外。在Mybatis中最出名的就是PageHelper 分頁(yè)插件,下面我們先來(lái)使用一下這個(gè)分頁(yè)插件。

本文主要內(nèi)容:

 

大多數(shù)框架都支持插件,用戶可通過(guò)編寫(xiě)插件來(lái)自行擴(kuò)展功能,Mybatis也不例外。

在Mybatis中最出名的就是PageHelper 分頁(yè)插件,下面我們先來(lái)使用一下這個(gè)分頁(yè)插件。

如何集成分頁(yè)插件

Spring-Boot+Mybatis+PageHelper 。

引入pom依賴

  1. <dependency> 
  2.    <groupId>com.github.pagehelper</groupId> 
  3.    <artifactId>pagehelper-spring-boot-starter</artifactId> 
  4.    <version>1.2.3</version> 
  5. </dependency> 

配置分頁(yè)插件配置項(xiàng)

  1. pagehelper: 
  2.   helperDialect: mysql 
  3.   reasonable: true 
  4.   supportMethodsArguments: true 
  5.   params: count=countSql 

service接口代碼中

  1. PageInfo selectUsersByName(int pageIndex, int pageSize); 

service實(shí)現(xiàn)類代碼中

  1. @Override 
  2. public PageInfo selectUsersByName(int pageIndex, int pageSize) { 
  3.     PageHelper.startPage(pageIndex, pageSize); 
  4.     List<User> users = userMapper.selectUsersByName(null); 
  5.     return new PageInfo(users); 

Mapper代碼代碼

  1. <select id="selectUsersByName" resultMap="User"
  2.     select * from m_user 
  3.     <where
  4.         <if test="userName != null and userName != ''"
  5.             `name` = #{userName} 
  6.         </if> 
  7.     </where
  8. </select
  1. List<User> selectUsersByName(@Param("userName") String userName); 

controller中代碼

  1. @GetMapping("/user/name"
  2. public PageInfo selectUsersByName(int pageIndex, int pageSize) { 
  3.     return userService.selectUsersByName(pageIndex, pageSize); 

然后我們?cè)L問(wèn)

http://localhost:9002/user/name?pageIndex=1&pageSize=10

輸出結(jié)果:

 

輸出重要項(xiàng)說(shuō)明:

  • pageNum:當(dāng)前頁(yè)碼。
  • pageSize:每頁(yè)數(shù)。
  • list:就是我們返回的業(yè)務(wù)數(shù)據(jù)。
  • total:總數(shù)據(jù)。
  • hasNextPage:是否存在下一頁(yè)。

我們?cè)诳纯摧敵鯯QL:

 

發(fā)現(xiàn)其實(shí)執(zhí)行了兩條SQL:count和limit。

猜測(cè)分頁(yè)插件實(shí)現(xiàn)

1.這個(gè)分頁(yè)插件無(wú)非就是在我們的查詢條件上拼接了個(gè)limit和做了一個(gè)count查詢。

2.我們這里使用的是Mysql作為數(shù)據(jù)庫(kù),如果是Oracle的話那就不是limit了,所以這里有多重?cái)?shù)據(jù)庫(kù)對(duì)應(yīng)的方案。

3.在沒(méi)有此插件的前面攔截并做了sql和相關(guān)處理。

根據(jù)官網(wǎng)快速入門(mén)插件

下面是來(lái)自官網(wǎng)的一段話:

MyBatis 允許你在映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis 允許使用插件來(lái)攔截的方法調(diào)用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

這些類中方法的細(xì)節(jié)可以通過(guò)查看每個(gè)方法的簽名來(lái)發(fā)現(xiàn),或者直接查看 MyBatis 發(fā)行包中的源代碼。如果你想做的不僅僅是監(jiān)控方法的調(diào)用,那么你最好相當(dāng)了解要重寫(xiě)的方法的行為。因?yàn)樵谠噲D修改或重寫(xiě)已有方法的行為時(shí),很可能會(huì)破壞 MyBatis 的核心模塊。這些都是更底層的類和方法,所以使用插件的時(shí)候要特別當(dāng)心。

通過(guò) MyBatis 提供的強(qiáng)大機(jī)制,使用插件是非常簡(jiǎn)單的,只需實(shí)現(xiàn) Interceptor 接口,并指定想要攔截的方法簽名即可。

那我們就嘗試著按照官方來(lái)寫(xiě)一個(gè)插件。

自定義插件

  1. @Intercepts({@Signature( 
  2.         type= Executor.class, 
  3.         method = "update"
  4.         args = {MappedStatement.class,Object.class})}) 
  5. public class TianPlugin implements Interceptor { 
  6.     private Properties properties = new Properties(); 
  7.  
  8.     @Override 
  9.     public Object intercept(Invocation invocation) throws Throwable { 
  10.         System.out.println("老田寫(xiě)的一個(gè)Mybatis插件--start"); 
  11.         Object returnObject = invocation.proceed(); 
  12.         System.out.println("老田寫(xiě)的一個(gè)Mybatis插件---end"); 
  13.         return returnObject; 
  14.     } 

然后把插件類注入到容器中。

 

這里的自定義完全是官網(wǎng)給出的案例。從自定義的插件類中看到有個(gè)update,我們猜測(cè)肯定是需要執(zhí)行update才會(huì)被攔截到。

訪問(wèn)前面的代碼:http://localhost:9002/updateUser

 

成功了。

這是大家肯定會(huì)聯(lián)想到我們剛剛開(kāi)始學(xué)動(dòng)態(tài)代理的時(shí)候,不就是在要調(diào)用的方法的前面和后面做點(diǎn)小東東嗎?

Mybatis的插件確實(shí)就是這樣的。

我們來(lái)分析一下官方的那段話和我們自定義的插件。

分析

首先,我們自定義的插件必須是針對(duì)下面這四個(gè)類以及方法。

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

其次,我們必須實(shí)現(xiàn)Mybatis的Interceptor。

 

Interceptor中三個(gè)方法的作用:

  • intercept():執(zhí)行攔截內(nèi)容的地方,比如:在調(diào)用某類方法前后做一些自己的處理,簡(jiǎn)單就是打印日志。
  • plugin():決定是否觸發(fā)intercept()方法。
  • setProperties():給自定義的攔截器傳遞我們配置的屬性參數(shù)(這個(gè)可以暫時(shí)不管他,后面我們寫(xiě)一個(gè)相對(duì)完整點(diǎn)的插件,你就明白是干啥的了)。

plugin方法

  1. default Object plugin(Object target) { 
  2.     return Plugin.wrap(target, this); 
  3.   } 

默認(rèn)實(shí)現(xiàn)方法,里面調(diào)用了Plugin.wrap()方法。

  1. public class Plugin implements InvocationHandler { 
  2.  
  3.   private Object target; 
  4.   private Interceptor interceptor; 
  5.   private Map<Class<?>, Set<Method>> signatureMap; 
  6.  
  7.   private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { 
  8.     this.target = target; 
  9.     this.interceptor = interceptor; 
  10.     this.signatureMap = signatureMap; 
  11.   } 
  12.  
  13.   public static Object wrap(Object target, Interceptor interceptor) { 
  14.     Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); 
  15.     Class<?> type = target.getClass(); 
  16.     Class<?>[] interfaces = getAllInterfaces(type, signatureMap); 
  17.     if (interfaces.length > 0) { 
  18.       // 創(chuàng)建JDK動(dòng)態(tài)代理對(duì)象 
  19.       return Proxy.newProxyInstance( 
  20.           type.getClassLoader(), 
  21.           interfaces, 
  22.           new Plugin(target, interceptor, signatureMap)); 
  23.     } 
  24.     return target; 
  25.   } 
  26.  
  27.   @Override 
  28.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  29.     try { 
  30.       Set<Method> methods = signatureMap.get(method.getDeclaringClass()); 
  31.       // 判斷是否是需要攔截的方法(很重要) 
  32.       if (methods != null && methods.contains(method)) { 
  33.         // 回調(diào)intercept()方法 
  34.         return interceptor.intercept(new Invocation(target, method, args)); 
  35.       } 
  36.       return method.invoke(target, args); 
  37.     } catch (Exception e) { 
  38.       throw ExceptionUtil.unwrapThrowable(e); 
  39.     } 
  40.   } 
  41. //...省略其他不相關(guān)代碼 

這不就是一個(gè)JDK動(dòng)態(tài)代理嗎?

Map

所以,我們不要?jiǎng)硬粍?dòng)就說(shuō)反射性能很差,那是因?yàn)槟銢](méi)有像Mybatis一樣去緩存一個(gè)對(duì)象的反射結(jié)果。

判斷是否是需要攔截的方法,這句注釋很重要,一旦忽略了,都不知道Mybatis是怎么判斷是否執(zhí)行攔截內(nèi)容的,要記住。

Plugin.wrap(target, this)是干什么的?

使用JDK的動(dòng)態(tài)代理,給target對(duì)象創(chuàng)建一個(gè)delegate代理對(duì)象,以此來(lái)實(shí)現(xiàn)方法攔截和增強(qiáng)功能,它會(huì)回調(diào)intercept()方法。

為什么要寫(xiě)注解?注解都是什么含義?

在我們自定義的插件上有一堆注解,別害怕。

Mybatis規(guī)定插件必須編寫(xiě)Annotation注解,是必須,而不是可選。

  1. @Intercepts({@Signature( type= Executor.class, method = "update"
  2.                         args = {MappedStatement.class,Object.class})} 
  3.            ) 
  4. public class TianPlugin implements Interceptor { 

@Intercepts注解:裝載一個(gè)@Signature列表,一個(gè)@Signature其實(shí)就是一個(gè)需要攔截的方法封裝。那么,一個(gè)攔截器要攔截多個(gè)方法,自然就是一個(gè)@Signature列表。

  1. type= Executor.class, method = "update",args = {MappedStatement.class,Object.class} 

解釋:要攔截Executor接口內(nèi)的query()方法,參數(shù)類型為args列表。

 

那如果想攔截多個(gè)方法呢?

  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target(ElementType.TYPE) 
  4. public @interface Intercepts { 
  5.   Signature[] value(); 

這就簡(jiǎn)單了吧,我們?cè)贎Intercepts注解中可以存放多個(gè)@Signature注解。

比如說(shuō)前面分頁(yè)插件中就是攔截多個(gè)方法的。

 

為什么攔截兩個(gè)都是query方法呢?因?yàn)樵贓xecutor中有兩個(gè)query方法。

 

總結(jié)下:

Mybatis規(guī)定必須使用@Intercepts注解。

@Intercepts注解內(nèi)可以添加多個(gè)類多個(gè)方法,注意方法名和參數(shù)類型個(gè)數(shù)一定要對(duì)應(yīng)起來(lái)。

本文轉(zhuǎn)載自微信公眾號(hào)「 Java后端技術(shù)全棧」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 Java后端技術(shù)全棧公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java后端技術(shù)全棧
相關(guān)推薦

2022-12-26 08:19:06

Mybatis核心類工廠類

2020-12-18 08:03:00

插件MyBatis Executor

2021-08-09 11:15:28

MybatisJavaSpring

2024-02-26 00:00:00

Docker容器

2010-03-25 16:08:19

2019-11-25 16:05:20

MybatisPageHelpeJava

2021-01-18 06:53:31

QQ牛角圖標(biāo)聊天軟件

2017-08-09 15:07:08

大數(shù)據(jù)數(shù)據(jù)分析戶畫(huà)像

2020-12-15 08:03:57

Mybatis配置文件

2023-04-28 08:30:56

MyBatis架構(gòu)API

2021-12-09 18:32:08

Chrome插件瀏覽器

2025-04-22 08:44:15

2023-07-29 22:02:06

MyBatis數(shù)據(jù)庫(kù)配置

2018-10-08 09:44:51

無(wú)線AP故障

2021-06-30 15:05:15

VS Code程序員編程

2011-08-25 09:30:22

2019-12-19 08:56:21

MybatisSQL執(zhí)行器

2019-01-23 13:04:09

QLCNAND閃存

2020-03-03 20:04:30

SSD硬盤(pán)閃存

2018-03-09 10:02:23

iPhone X下巴蘋(píng)果
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产ts人妖一区二区三区 | 久久国内精品 | 国产精品不卡一区 | 国产真实精品久久二三区 | 国产欧美精品一区二区 | 精品久久香蕉国产线看观看亚洲 | 日韩中文字幕 | 久草视| 成人在线观看网址 | 亚洲国产精品激情在线观看 | 亚洲第一视频 | 一区二区三区国产精品 | av网站在线免费观看 | 欧美日韩精品一区二区三区视频 | 日本一区二区三区精品视频 | 精品国产欧美日韩不卡在线观看 | 麻豆av网| 久草视频在线播放 | 日韩精品一区二区三区中文在线 | 欧美a在线观看 | 久久久久久久综合 | 成年人精品视频 | 中文字幕一级毛片 | 国内精品视频免费观看 | 久久久久免费精品国产小说色大师 | www.日韩 | 日韩成人在线网站 | avav在线看 | 97精品一区二区 | www.黄网| 久久久123| 天天av天天好逼 | 中文字幕日韩欧美一区二区三区 | 黄网站在线观看 | 中文字幕欧美日韩 | 精品久久99 | 国产亚洲精品久久久久久豆腐 | 在线一区二区三区 | 久久99久久久久 | 欧美日韩久久久 | 欧美福利在线 |