mybatis多個(gè)plugins的執(zhí)行順序解析
一、前言
在mybatis官網(wǎng)中,有插件一說(shuō) mybatis plugins 如果同時(shí)有多個(gè)插件,那么他們的執(zhí)行順序是怎樣的?
二、準(zhǔn)備工作、代碼準(zhǔn)備
1、 項(xiàng)目結(jié)構(gòu)
2、TestDAO
public interface TestDAO { Test selectById(Integer id); default void testDefaultMethod(){ System.out.println("===調(diào)用接口中的默認(rèn)方法,用來(lái)驗(yàn)證MapperProxy中的isDefaultMethod方法==="); } }
3、Test
@Data @NoArgsConstructor @AllArgsConstructor public class Test { private Integer id; private String name; }
4、ExamplePlugin
@Intercepts({@Signature( type= Executor.class, method = "query", args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class} ), @Signature( type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class} ), @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class} ) }) public class ExamplePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("==== ExamplePlugin 開(kāi)始搞事情:" + invocation.getMethod().getName() + " ===="); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
5、SecondExamplePlugin
@Intercepts({@Signature( type= Executor.class, method = "query", args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class} ), @Signature( type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class} ), @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class} ) }) public class SecondExamplePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("==== SecondExamplePlugin 開(kāi)始搞事情:" + invocation.getMethod().getName() + " ===="); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
6、Main
public class Main { public static SqlSession getSqlSession() throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory.openSession(); } public static void main(String[] args) throws IOException { TestDAO testDAO = getSqlSession().getMapper(TestDAO.class); Test test = testDAO.selectById(1); // testDAO.testDefaultMethod(); //類(lèi)文件是緩存在java虛擬機(jī)中,我們將類(lèi)文件打印到文件中,便于查看 // generateProxyFile("F:/TestDAOProxy.class"); } private static void generateProxyFile(String path){ byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{TestDAO.class}); try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理類(lèi)class文件寫(xiě)入成功"); } catch (Exception e) { System.out.println("寫(xiě)文件錯(cuò)誤"); } } }
7、 TestMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.me.mybatis.dao.TestDAO"> <resultMap id="testMap" type="com.me.mybatis.domain.Test"> <result property="id" column="id" /> <result property="name" column="name" /> </resultMap> <sql id="allColumn"> id, name </sql> <select id="selectById" resultMap="testMap"> SELECT <include refid="allColumn"/> FROM test WHERE id = #{id} </select> </mapper>
8、mybatis-confi.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.me.mybatis.plugin.ExamplePlugin"> <property name="someProperty" value="200" /> </plugin> <plugin interceptor="com.me.mybatis.plugin.SecondExamplePlugin"> <property name="someProperty" value="200" /> </plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="NewPwd@123"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/TestMapper.xml"/> </mappers> </configuration>
9、POM
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.me</groupId> <artifactId>mybatis-test</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.14</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency> </dependencies> </project>
三、開(kāi)始探索
1、運(yùn)行結(jié)果
==== SecondExamplePlugin 開(kāi)始搞事情:query ====
==== ExamplePlugin 開(kāi)始搞事情:query ====
==== SecondExamplePlugin 開(kāi)始搞事情:prepare ====
==== ExamplePlugin 開(kāi)始搞事情:prepare ====
==== SecondExamplePlugin 開(kāi)始搞事情:setParameters ====
==== ExamplePlugin 開(kāi)始搞事情:setParameters ====
==== SecondExamplePlugin 開(kāi)始搞事情:handleResultSets ====
==== ExamplePlugin 開(kāi)始搞事情:handleResultSets ====
2、疑問(wèn):為什么是這樣的順序?
和我們?cè)趍ybatis-config.xml文件中的順序相反,為什么?
3、注釋掉一個(gè),我們從一個(gè)plugin開(kāi)始debug,看看做了什么
4、如圖,在Configuration的四個(gè)方法newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor中打上斷點(diǎn)
5、debug Main類(lèi)的main方法
6、我們發(fā)現(xiàn)在newExecutor中,被攔住了
這里的interceptorChain是什么東西?我們往上找一找,發(fā)現(xiàn)它是在Configuration類(lèi)中new出來(lái)的。它等價(jià)于mybatis-config中的<plugins></plugins>
7、我們已經(jīng)知道interceptorChain是什么了,那么進(jìn)入它的pluginAll方法
我們可以看到它是遍歷interceptors的plugin方法。而interceptors是ArrayList,是有序的。那么在配置文件中,哪個(gè)plugin在前,這里它就在前面
8、進(jìn)入interceptor的plugin方法,發(fā)現(xiàn)我們來(lái)到了我們自己寫(xiě)的ExamplePlugin類(lèi)的plugin方法
9、它又繼續(xù)調(diào)用了Plugin的靜態(tài)方法wrap
1) 第一步獲取@Signature注解中的type和method,也就是我們?cè)贓xamplePlugin中使用的注解。
2)第二步,用動(dòng)態(tài)代理,生成代理類(lèi)。其中Plugin作為InvocationHandler
10、UML圖
最終Executor不再是原來(lái)的類(lèi),而是它的代理類(lèi)。newStatementHandler方法和newResultSetHandler方法的流程,也差不多,最終也是生成代理類(lèi)。
當(dāng)Executor、StatementHandler、ParameterHandler、ResultSetHandler執(zhí)行他們自己的方法時(shí),實(shí)際上調(diào)用他們的代理類(lèi)Plugin中的invoke方法。
也就是在interceptor.intercept(new Invocation(target, method, args));這一句中,回到了我們ExamplePlugin的intercept方法
整個(gè)流程中Executor的代理。(這里只拿Executor來(lái)舉例)
四、結(jié)論
上面只是代理一次,還記得pluginAll嗎?
多個(gè)interceptor呢?當(dāng)然是代理類(lèi)又被代理了。
所以,后面的將會(huì)代理前面的,這也就是為什么SecondExamplePlugin先執(zhí)行的原因了——越外層的越先執(zhí)行嘛
多個(gè)插件的執(zhí)行順序已經(jīng)明了了,那么插件里面方法的執(zhí)行順序呢?
當(dāng)然是看這些方法什么時(shí)候被調(diào)用咯
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot整合Excel填充數(shù)據(jù)代碼示例
這篇文章主要給大家介紹了關(guān)于springboot整合Excel填充數(shù)據(jù)的相關(guān)資料,文中通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用springboot具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08Java實(shí)現(xiàn)音頻轉(zhuǎn)碼(WAV、MP3、AMR互轉(zhuǎn))
本文主要介紹了Java實(shí)現(xiàn)音頻轉(zhuǎn)碼,包括WAV、MP3、AMR互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02解析Jmeter脫離Jenkins后Ant集成郵件通知問(wèn)題
今天來(lái)講下本地的ant構(gòu)建并發(fā)送郵件。配置下來(lái)挺順利也挺簡(jiǎn)單的,對(duì)Jmeter脫離Jenkins后Ant集成郵件通知問(wèn)題感興趣的朋友跟隨小編一起看看吧2021-12-12Java模擬多線(xiàn)程實(shí)現(xiàn)搶票代碼實(shí)例
這篇文章主要介紹了Java模擬多線(xiàn)程實(shí)現(xiàn)搶票,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01Mybatis-plus實(shí)現(xiàn)join連表查詢(xún)的示例代碼
mybatis-plus在連表查詢(xún)上是不行的,如果需要連表查詢(xún),就得乖乖的去寫(xiě)xml文件了,本文介紹了mybatis-plus-join框架,它支持連表查詢(xún),感興趣的可以了解一下2023-08-08springboot集成spark并使用spark-sql的示例詳解
這篇文章主要介紹了spring-boot集成spark并使用spark-sql的方法,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02java模擬實(shí)現(xiàn)斗地主發(fā)牌小程序
這篇文章主要為大家詳細(xì)介紹了java模擬實(shí)現(xiàn)斗地主發(fā)牌小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04java面試LruCache?和?LinkedHashMap及算法實(shí)現(xiàn)
這篇文章主要為大家介紹了java面試LruCache?和?LinkedHashMap及算法實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Java中的集合ArrayList類(lèi)常用方法和遍歷
這篇文章主要介紹了Java中的集合ArrayList類(lèi)常用方法和遍歷,ArrayList 是大小可變的數(shù)組的實(shí)現(xiàn),存儲(chǔ)在內(nèi)的數(shù)據(jù)稱(chēng)為元素,此類(lèi)提供一些方法來(lái)操作內(nèi)部存儲(chǔ)的元素, ArrayList中可不斷添加元素,其大小也自動(dòng)增長(zhǎng),需要的朋友可以參考下2024-01-01