亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

超全MyBatis動態(tài)代理詳解(絕對干貨)

瀏覽:5日期:2023-10-20 09:17:48
前言

假如有人問你這么幾個問題,看能不能答上來

Mybatis Mapper 接口沒有實現(xiàn)類,怎么實現(xiàn)的動態(tài)代理 JDK 動態(tài)代理為什么不能對類進行代理(充話費送的問題) 抽象類可不可以進行 JDK 動態(tài)代理(附加問題)

超全MyBatis動態(tài)代理詳解(絕對干貨)

答不上來的鐵汁,證明 Proxy、Mybatis 源碼還沒看到位。不過沒有關(guān)系,繼續(xù)往下看就明白了

動態(tài)代理實戰(zhàn)

眾所周知哈,Mybatis 底層封裝使用的 JDK 動態(tài)代理。說 Mybatis 動態(tài)代理之前,先來看一下平常我們寫的動態(tài)代理 Demo,拋磚引玉

一般來說定義 JDK 動態(tài)代理分為三個步驟,如下所示

定義代理接口 定義代理接口實現(xiàn)類 定義動態(tài)代理調(diào)用處理器

三步代碼如下所示,玩過動態(tài)代理的小伙伴看過就能明白

public interface Subject { // 定義代理接口 String sayHello();}public class SubjectImpl implements Subject { // 定義代理接口實現(xiàn)類 @Override public String sayHello() { System.out.println(' Hello World'); return 'success'; }}public class ProxyInvocationHandler implements InvocationHandler { // 定義動態(tài)代理調(diào)用處理器 private Object target; public ProxyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(' 🧱 🧱 🧱 進入代理調(diào)用處理器 '); return method.invoke(target, args); }}

寫個測試程序,運行一下看看效果,同樣是分三步

創(chuàng)建被代理接口的實現(xiàn)類 創(chuàng)建動態(tài)代理類,說一下三個參數(shù) 類加載器 被代理類所實現(xiàn)的接口數(shù)組 調(diào)用處理器(調(diào)用被代理類方法,每次都經(jīng)過它) 被代理實現(xiàn)類調(diào)用方法

public class ProxyTest { public static void main(String[] args) { Subject subject = new SubjectImpl(); Subject proxy = (Subject) Proxy .newProxyInstance( subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); proxy.sayHello(); /** * 打印輸出如下 * 調(diào)用處理器:🧱 🧱 🧱 進入代理調(diào)用處理器 * 被代理實現(xiàn)類:Hello World */ }}

Demo 功能實現(xiàn)了,大致運行流程也清楚了,下面要針對原理實現(xiàn)展開分析

動態(tài)代理原理分析

從原理的角度上解析一下,上面動態(tài)代理測試程序是如何執(zhí)行的

第一步簡單明了, 創(chuàng)建了 Subject 接口的實現(xiàn)類 ,也是我們常規(guī)的實現(xiàn)

第二步是創(chuàng)建被代理對象的動態(tài)代理對象。這里有朋友就問了,怎么證明這是個動態(tài)代理對象?如圖所示

超全MyBatis動態(tài)代理詳解(絕對干貨)

JDK 動態(tài)代理對象名稱是有規(guī)則的,凡是經(jīng)過 Proxy 類生成的動態(tài)代理對象,前綴必然是 $Proxy ,后面的數(shù)字也是名稱組成部分

如果有小伙伴想要一探究竟, 關(guān)注 Proxy 內(nèi)部類 ProxyClassFactory ,這里會有想要的答案

超全MyBatis動態(tài)代理詳解(絕對干貨)

回歸正題,繼續(xù)看一下 ProxyInvocationHandler, 內(nèi)部保持了被代理接口實現(xiàn)類的引用 ,invoke 方法內(nèi)部使用反射調(diào)用被代理接口實現(xiàn)類方法

超全MyBatis動態(tài)代理詳解(絕對干貨)

可以看出生成的動態(tài)代理類,繼承了 Proxy 類,然后對 Subject 接口進行了實現(xiàn),而實現(xiàn)方法 sayHello 中實際調(diào)用的是 ProxyInvocationHandler 的 invoke 方法

一不小心發(fā)現(xiàn)了 JDK 動態(tài)代理不能對類進行代理的原因 ^ ^

也就是說,當我們調(diào)用 Subject#sayHello 時,方法調(diào)用鏈是這樣的

超全MyBatis動態(tài)代理詳解(絕對干貨)

但是,Demo 里有被代理接口的實現(xiàn)類,Mybatis Mapper 沒有,這要怎么玩

不知道不要緊,知道了估計也看不到這了,一起看下 mybatis 源碼是怎么玩的

mybatis version:3.4.x

Mybatis 源碼實現(xiàn)

不知道大家考沒考慮過這么一個問題, Mybatis Mapper 為什么不需要實現(xiàn)類?

假如說,我們項目使用的三層設(shè)計,Controller 控制請求接收,Service 負責業(yè)務處理,Mapper 負責數(shù)據(jù)庫交互

超全MyBatis動態(tài)代理詳解(絕對干貨)

Mapper 層也就是我們常說的數(shù)據(jù)庫映射層,負責對數(shù)據(jù)庫的操作,比如對數(shù)據(jù)的查詢或者新增、刪除等

大膽設(shè)想下,項目沒有使用 Mybatis,需要在 Mapper 實現(xiàn)層寫數(shù)據(jù)庫交互,會寫一些什么內(nèi)容?

會寫一些常規(guī)的 JDBC 操作,比如:

// 裝載Mysql驅(qū)動Class.forName(driveName);// 獲取連接con = DriverManager.getConnection(url, user, pass);// 創(chuàng)建StatementStatement state = con.createStatement();// 構(gòu)建SQL語句String stuQuerySqlStr = 'SELECT * FROM student';// 執(zhí)行SQL返回結(jié)果ResultSet result = state.executeQuery(stuQuerySqlStr);...

如果項目中所有 Mapper 實現(xiàn)層都要這么玩,那豈不是很想打人...

超全MyBatis動態(tài)代理詳解(絕對干貨)

所以 Mybatis 結(jié)合項目痛點,應運而生,怎么做的呢

將所有和 JDBC 交互的操作,底層采用 JDK 動態(tài)代理封裝,使用者只需要自定義 Mapper 和 .xml 文件 SQL 語句定義在 .xml 文件或者 Mapper 中,項目啟動時通過解析器解析 SQL 語句組裝為 Java 中的對象

解析器分為多種,因為 Mybatis 中不僅有靜態(tài)語句,同時也包含動態(tài) SQL 語句

這也就是為什么 Mapper 接口不需要實現(xiàn)類, 因為都已經(jīng)被 Mybatis 通過動態(tài)代理封裝了,如果每個 Mapper 都來一個實現(xiàn)類,臃腫且無用 。經(jīng)過這一頓操作,展示給我們的就是項目里用到的 Mybatis 框架

上面鋪墊這么久,終于要到主角了, 為什么 Mybatis Mapper 接口沒有實現(xiàn)類也可以實現(xiàn)動態(tài)代理

想要嚴格按照先后順序介紹 Mybatis 動態(tài)代理流程,而不超前引用未介紹過的術(shù)語,這幾乎是不可能的,筆者盡量說的通俗易懂

無實現(xiàn)類完成動態(tài)代理

核心點來了,拿起小本本坐板正了

超全MyBatis動態(tài)代理詳解(絕對干貨)

我們先來看下普通動態(tài)代理有沒有可能不用實現(xiàn)類,僅靠接口完成

public interface Subject { String sayHello();}public class ProxyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(' 🧱 🧱 🧱 進入代理調(diào)用處理器 '); return 'success'; }}

根據(jù)代碼可以看到,我們并沒有實現(xiàn)接口 Subject,繼續(xù)看一下怎么實現(xiàn)動態(tài)代理

public class ProxyTest { public static void main(String[] args) { Subject proxy = (Subject) Proxy .newProxyInstance( subject.getClass().getClassLoader(), new Class[]{Subject.class}, new ProxyInvocationHandler()); proxy.sayHello(); /** * 打印輸出如下 * 調(diào)用處理器:🧱 🧱 🧱 進入代理調(diào)用處理器 */ }}

可以看到,對比文初的 Demo,這里對 Proxy.newProxyInstance 方法的參數(shù)作出了變化

之前是通過實現(xiàn)類獲取所實現(xiàn)接口的 Class 數(shù)組,而這里是把接口本身放到 Class 數(shù)組中,殊歸同途

有實現(xiàn)類接口和無實現(xiàn)類接口產(chǎn)生的動態(tài)代理類有什么區(qū)別

有實現(xiàn)類接口是對 InvocationHandler#invoke 方法調(diào)用,invoke 方法通過反射調(diào)用被代理對象(SubjectImpl)方法(sayHello) 無實現(xiàn)類接口則是僅對 InvocationHandler#invoke 產(chǎn)生調(diào)用。 所以有實現(xiàn)類接口返回的是被代理對象接口返回值,而無實現(xiàn)類接口返回的僅是 invoke 方法返回值

InvocationHandler#invoke 方法返回值是 success 字符串,定義個字符串變量,是否能成功返回

超全MyBatis動態(tài)代理詳解(絕對干貨)

現(xiàn)在第一個問題答案已經(jīng)浮現(xiàn), Mapper 沒有實現(xiàn)類,所有調(diào)用 JDBC 等操作都是在 Mybatis InvocationHandler 實現(xiàn)的

問題既然已經(jīng)得到了解決,給人一種感覺,好像沒那么難,但是你不好奇,Mybatis 底層怎么做的么?

超全MyBatis動態(tài)代理詳解(絕對干貨)

先拋出一個問題,然后帶著問題去看源碼,可能讓你記憶 Double 倍深刻

咱們 Demo 里的接口是固定的,Mybatis Mapper 可是不固定的,怎么搞?

Mybatis 是這么說的

超全MyBatis動態(tài)代理詳解(絕對干貨)

看看 Mybatis 底層它怎么實現(xiàn)的動態(tài)接口代理,小伙伴只需要關(guān)注標記處的代碼即可

超全MyBatis動態(tài)代理詳解(絕對干貨)

和我們的 Demo 代碼很像,核心點在于 mapperInterface 它是怎么賦值的

先來說一下 Mybatis 代理工廠中具體生成動態(tài)代理類具體邏輯

根據(jù) .xml 上關(guān)聯(lián)的 namespace, 通過 Class#forName 反射的方式返回 Class 對象(不止 .xml namespace 一種方式) 將得到的 Class 對象(實際就是接口對象)傳遞給 Mybatis 代理工廠生成代理對象,也就是剛才 mapperInterface 屬性

謎底揭曉,Mybatis 使用接口全限定名通過 Class#forName 生成 Class 對象,這個 Class 對象類型就是接口

為了方便大家理解,通過 Mybatis 源碼提供的測試類舉例。假設(shè)已有接口 AutoConstructorMapper 以及對應的 .xml 如下

超全MyBatis動態(tài)代理詳解(絕對干貨)

超全MyBatis動態(tài)代理詳解(絕對干貨)

執(zhí)行第一步,根據(jù) .xml namespace 得到 Class 對象

超全MyBatis動態(tài)代理詳解(絕對干貨)

首先第一步獲取 .xml 上 mapper 標簽 namespace 屬性,得到 mapper 接口全限定信息 根據(jù) mapper 全限定信息獲取 Class 對象 添加到對應的映射器容器中,等待生成動態(tài)代理對象

如果此時調(diào)用生成動態(tài)代理對象,代理工廠 newInstance 方法如下:

超全MyBatis動態(tài)代理詳解(絕對干貨)

至此,文初提的 Proxy、Mybatis 動態(tài)代理相關(guān)問題已全部答疑

抽象類能否 JDK 動態(tài)代理

說代碼前結(jié)論先行, 不能!

public abstract class AbstractProxy { abstract void sayHello();}AbstractProxy proxyInterface = (AbstractProxy) Proxy .newProxyInstance( ProxyTest.class.getClassLoader(), new Class[]{AbstractProxy.class}, new ProxyInvocationHandler());proxyInterface.sayHello();

毫無疑問,報錯是必然的,JDK 是不能對類進行代理的

超全MyBatis動態(tài)代理詳解(絕對干貨)

帶著小疑惑我們看一下 Proxy 源碼報錯位置,JDK 動態(tài)代理在生成代理類的過程代碼中,會有是否接口驗證

超全MyBatis動態(tài)代理詳解(絕對干貨)

抽象類終歸是類,加個 abstract 也成不了接口(就像我,雖然胖了 60 斤,但依然是帥哥)

下次面試官如果有問這問題的, 斬釘截鐵一點 ,就是不能

結(jié)言

結(jié)合 Mybatis 使用 JDK 動態(tài)代理相關(guān)的問題,展開了文章的講述,這里總結(jié)如下

Q:JDK 動態(tài)代理能否對類代理?

因為 JDK 動態(tài)代理生成的代理類,會繼承 Proxy 類,由于 Java 無法多繼承,所以無法對類進行代理

Q:抽象類是否可以 JDK 動態(tài)代理?

不可以,抽象類本質(zhì)上也是類,Proxy 生成代理類過程中,會校驗傳入 Class 是否接口

Q:Mybatis Mapper 接口沒有實現(xiàn)類,怎么實現(xiàn)的動態(tài)代理?

Mybatis 會通過 Class#forname 得到 Mapper 接口 Class 對象,生成對應的動態(tài)代理對象,核心業(yè)務處理都會在 InvocationHandler#invoke 進行處理

到此這篇關(guān)于超全MyBatis動態(tài)代理詳解(絕對干貨)的文章就介紹到這了,更多相關(guān)MyBatis 動態(tài)代理內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

相關(guān)文章:
主站蜘蛛池模板: 91精品啪国产在线观看免费牛牛 | 日韩永久在线观看免费视频 | 91极品视频 | 自拍偷拍视频网站 | 亚洲欧洲日韩国产一区二区三区 | 91亚洲国产成人精品性色 | 欧美日韩国产中文字幕 | 手机看片www xiao2b cm | 黄频在线看 | 视频一区免费 | 免费看的黄色大片 | 国产精品欧美亚洲韩国日本99 | 日本免费的一级绿象 | 久久九色综合九色99伊人 | 95视频在线观看免费 | 爱爱男女在线观看视频高清 | 日本一级二级三级久久 | 午夜精品视频在线观看 | 日韩欧美一级a毛片欧美一级 | 啪啪成人 | 精品三级内地国产在线观看 | 国产高清天干天天美女 | 一级毛片在线完整观看 | 大黄网站在线观看 | 免费成年人视频 | 精品国产福利一区二区在线 | 久久久久久全国免费观看 | 成人国产视频在线观看 | 高清欧美日韩一区二区三区在线观看 | 国产亚洲图片 | 性刺激视频在线观看免费 | 国内精品久久久久激情影院 | 国产综合精品一区二区 | 九九精品免视频国产成人 | 中文在线播放 | 日本国产在线观看 | 久久成人影视 | 1024人成网色www | 69国产成人精品午夜福中文 | a4yy午夜| 免费观看拍拍视频在线观看 |