欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

超全MyBatis動(dòng)態(tài)代理詳解(絕對(duì)干貨)

 更新時(shí)間:2021年02月02日 09:30:29   作者:六胖  
這篇文章主要介紹了超全MyBatis動(dòng)態(tài)代理詳解(絕對(duì)干貨),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

假如有人問(wèn)你這么幾個(gè)問(wèn)題,看能不能答上來(lái)

  • Mybatis Mapper 接口沒(méi)有實(shí)現(xiàn)類,怎么實(shí)現(xiàn)的動(dòng)態(tài)代理
  • JDK 動(dòng)態(tài)代理為什么不能對(duì)類進(jìn)行代理(充話費(fèi)送的問(wèn)題)
  • 抽象類可不可以進(jìn)行 JDK 動(dòng)態(tài)代理(附加問(wèn)題)

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

動(dòng)態(tài)代理實(shí)戰(zhàn)

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

一般來(lái)說(shuō)定義 JDK 動(dòng)態(tài)代理分為三個(gè)步驟,如下所示

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

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

public interface Subject { // 定義代理接口
 String sayHello();
}

public class SubjectImpl implements Subject { // 定義代理接口實(shí)現(xiàn)類
 @Override
 public String sayHello() {
  System.out.println(" Hello World");
  return "success";
 }
}

public class ProxyInvocationHandler implements InvocationHandler { // 定義動(dòng)態(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(" 🧱 🧱 🧱 進(jìn)入代理調(diào)用處理器 ");
  return method.invoke(target, args);
 }
}

寫個(gè)測(cè)試程序,運(yùn)行一下看看效果,同樣是分三步

  • 創(chuàng)建被代理接口的實(shí)現(xiàn)類
  • 創(chuàng)建動(dòng)態(tài)代理類,說(shuō)一下三個(gè)參數(shù)
    • 類加載器
    • 被代理類所實(shí)現(xiàn)的接口數(shù)組
    • 調(diào)用處理器(調(diào)用被代理類方法,每次都經(jīng)過(guò)它)
  • 被代理實(shí)現(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)用處理器:🧱 🧱 🧱 進(jìn)入代理調(diào)用處理器
   * 被代理實(shí)現(xiàn)類:Hello World
   */
 }
}

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

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

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

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

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

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

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

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

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

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

也就是說(shuō),當(dāng)我們調(diào)用 Subject#sayHello 時(shí),方法調(diào)用鏈?zhǔn)沁@樣的

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

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

mybatis version:3.4.x

Mybatis 源碼實(shí)現(xiàn)

不知道大家考沒(méi)考慮過(guò)這么一個(gè)問(wèn)題, Mybatis Mapper 為什么不需要實(shí)現(xiàn)類?

假如說(shuō),我們項(xiàng)目使用的三層設(shè)計(jì),Controller 控制請(qǐng)求接收,Service 負(fù)責(zé)業(yè)務(wù)處理,Mapper 負(fù)責(zé)數(shù)據(jù)庫(kù)交互

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

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

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

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

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

所以 Mybatis 結(jié)合項(xiàng)目痛點(diǎn),應(yīng)運(yùn)而生,怎么做的呢

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

解析器分為多種,因?yàn)?Mybatis 中不僅有靜態(tài)語(yǔ)句,同時(shí)也包含動(dòng)態(tài) SQL 語(yǔ)句

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

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

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

無(wú)實(shí)現(xiàn)類完成動(dòng)態(tài)代理

核心點(diǎn)來(lái)了,拿起小本本坐板正了

我們先來(lái)看下普通動(dòng)態(tài)代理有沒(méi)有可能不用實(shí)現(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(" 🧱 🧱 🧱 進(jìn)入代理調(diào)用處理器 ");
  return "success";
 }
}

根據(jù)代碼可以看到,我們并沒(méi)有實(shí)現(xiàn)接口 Subject,繼續(xù)看一下怎么實(shí)現(xiàn)動(dòng)態(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)用處理器:🧱 🧱 🧱 進(jìn)入代理調(diào)用處理器
   */
 }
}

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

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

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

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

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

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

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

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

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

Mybatis 是這么說(shuō)的

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

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

先來(lái)說(shuō)一下 Mybatis 代理工廠中具體生成動(dòng)態(tài)代理類具體邏輯

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

謎底揭曉,Mybatis 使用接口全限定名通過(guò) Class#forName 生成 Class 對(duì)象,這個(gè) Class 對(duì)象類型就是接口

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

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

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

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

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

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

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

public abstract class AbstractProxy {
 abstract void sayHello();
}

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

毫無(wú)疑問(wèn),報(bào)錯(cuò)是必然的,JDK 是不能對(duì)類進(jìn)行代理的

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

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

下次面試官如果有問(wèn)這問(wèn)題的, 斬釘截鐵一點(diǎn) ,就是不能

結(jié)言

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

Q:JDK 動(dòng)態(tài)代理能否對(duì)類代理?

因?yàn)?JDK 動(dòng)態(tài)代理生成的代理類,會(huì)繼承 Proxy 類,由于 Java 無(wú)法多繼承,所以無(wú)法對(duì)類進(jìn)行代理

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

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

Q:Mybatis Mapper 接口沒(méi)有實(shí)現(xiàn)類,怎么實(shí)現(xiàn)的動(dòng)態(tài)代理?

Mybatis 會(huì)通過(guò) Class#forname 得到 Mapper 接口 Class 對(duì)象,生成對(duì)應(yīng)的動(dòng)態(tài)代理對(duì)象,核心業(yè)務(wù)處理都會(huì)在 InvocationHandler#invoke 進(jìn)行處理

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

相關(guān)文章

  • 關(guān)于jvm的垃圾回收器以及觸發(fā)full gc的場(chǎng)景

    關(guān)于jvm的垃圾回收器以及觸發(fā)full gc的場(chǎng)景

    這篇文章主要介紹了關(guān)于jvm的垃圾回收器以及觸發(fā)full gc的場(chǎng)景,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • @Scheduled定時(shí)器原理及@RefreshScope相互影響

    @Scheduled定時(shí)器原理及@RefreshScope相互影響

    這篇文章主要為大家介紹了@Scheduled定時(shí)器原理及@RefreshScope相互影響詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 詳解Java中的Lambda表達(dá)式

    詳解Java中的Lambda表達(dá)式

    Lambda表達(dá)式是Java SE 8中一個(gè)重要的新特性。這篇文章主要介紹了Java中的Lambda表達(dá)式 ,需要的朋友可以參考下
    2019-04-04
  • 阿里的Easyexcel讀取Excel文件的方法(最新版本)

    阿里的Easyexcel讀取Excel文件的方法(最新版本)

    這篇文章主要介紹了阿里的Easyexcel讀取Excel文件(最新版本)的方法,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • 解決RedisTemplate的key默認(rèn)序列化器的問(wèn)題

    解決RedisTemplate的key默認(rèn)序列化器的問(wèn)題

    這篇文章主要介紹了解決RedisTemplate的key默認(rèn)序列化器的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • springboot中如何使用@Async方法

    springboot中如何使用@Async方法

    這篇文章主要介紹了springboot中如何使用@Async方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Springboot詳解整合SpringSecurity實(shí)現(xiàn)全過(guò)程

    Springboot詳解整合SpringSecurity實(shí)現(xiàn)全過(guò)程

    Spring Security基于Spring開發(fā),項(xiàng)目中如果使用Springboot作為基礎(chǔ),配合Spring Security做權(quán)限更加方便,而Shiro需要和Spring進(jìn)行整合開發(fā)。因此作為spring全家桶中的Spring Security在java領(lǐng)域很常用
    2022-07-07
  • java 計(jì)算中位數(shù)的實(shí)現(xiàn)方法

    java 計(jì)算中位數(shù)的實(shí)現(xiàn)方法

    這篇文章主要介紹了java 計(jì)算中位數(shù)的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • 使用spring整合Quartz實(shí)現(xiàn)—定時(shí)器功能

    使用spring整合Quartz實(shí)現(xiàn)—定時(shí)器功能

    這篇文章主要介紹了使用spring整合Quartz實(shí)現(xiàn)—定時(shí)器功能,不基于特定的基類的方法,需要的朋友可以參考下
    2018-04-04
  • Kotlin實(shí)現(xiàn)靜態(tài)方法

    Kotlin實(shí)現(xiàn)靜態(tài)方法

    這篇文章主要介紹了Kotlin實(shí)現(xiàn)靜態(tài)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05

最新評(píng)論