最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

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

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

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

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

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

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

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

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)代理類,說一下三個(gè)參數(shù)
    • 類加載器
    • 被代理類所實(shí)現(xiàn)的接口數(shù)組
    • 調(diào)用處理器(調(diào)用被代理類方法,每次都經(jīng)過它)
  • 被代理實(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í)行的

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

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

JDK 動(dòng)態(tài)代理對(duì)象名稱是有規(guī)則的,凡是經(jīng)過 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)行代理的原因 ^ ^

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

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

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

mybatis version:3.4.x

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

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

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

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

大膽設(shè)想下,項(xiàng)目沒有使用 Mybatis,需要在 Mapper 實(shí)現(xiàn)層寫數(shù)據(jù)庫交互,會(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語句
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 語句定義在 .xml 文件或者 Mapper 中,項(xiàng)目啟動(dòng)時(shí)通過解析器解析 SQL 語句組裝為 Java 中的對(duì)象

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

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

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

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

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

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

我們先來看下普通動(dòng)態(tà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ù)代碼可以看到,我們并沒有實(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ù)作出了變化

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

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

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

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

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

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

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

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

Mybatis 是這么說的

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

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

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

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

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

為了方便大家理解,通過 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)問題已全部答疑

抽象類能否 JDK 動(dòng)態(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();

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

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

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

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

結(jié)言

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

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

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

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

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

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

Mybatis 會(huì)通過 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)文章

  • 深入同步訪問共享的可變數(shù)據(jù)分析

    深入同步訪問共享的可變數(shù)據(jù)分析

    本篇文章是對(duì)同步訪問共享的可變數(shù)據(jù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • springboot整合mybatis實(shí)現(xiàn)多表查詢的實(shí)戰(zhàn)記錄

    springboot整合mybatis實(shí)現(xiàn)多表查詢的實(shí)戰(zhàn)記錄

    SpringBoot對(duì)數(shù)據(jù)庫操作有多種方式,下面這篇文章主要給大家介紹了關(guān)于springboot整合mybatis實(shí)現(xiàn)多表查詢的相關(guān)資料,文中通過示例代碼以及圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • Hibernate validator使用以及自定義校驗(yàn)器注解

    Hibernate validator使用以及自定義校驗(yàn)器注解

    這篇文章主要介紹了Hibernate validator使用以及自定義校驗(yàn)器注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • JavaWeb實(shí)現(xiàn)多文件上傳及zip打包下載

    JavaWeb實(shí)現(xiàn)多文件上傳及zip打包下載

    這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)多文件上傳及zip打包下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Java二維數(shù)組與稀疏數(shù)組相互轉(zhuǎn)換實(shí)現(xiàn)詳解

    Java二維數(shù)組與稀疏數(shù)組相互轉(zhuǎn)換實(shí)現(xiàn)詳解

    在某些應(yīng)用場(chǎng)景中需要大量的二維數(shù)組來進(jìn)行數(shù)據(jù)存儲(chǔ),但是二維數(shù)組中卻有著大量的無用的位置占據(jù)著內(nèi)存空間,稀疏數(shù)組就是為了優(yōu)化二維數(shù)組,節(jié)省內(nèi)存空間
    2022-09-09
  • MybatisPlus逆向工程的項(xiàng)目實(shí)踐

    MybatisPlus逆向工程的項(xiàng)目實(shí)踐

    Mybatis-Plus逆向工程,是MP官方提供的一款代碼生成器,可以自動(dòng)生成對(duì)應(yīng)的實(shí)體類、Mapper接口和配置文件,,本文主要介紹了MybatisPlus逆向工程的項(xiàng)目實(shí)踐,感興趣的可以了解一下
    2024-03-03
  • 詳解spring cloud分布式日志鏈路跟蹤

    詳解spring cloud分布式日志鏈路跟蹤

    這篇文章主要介紹了詳解spring cloud分布式日志鏈路跟蹤,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • 解決Spring Cloud feign GET請(qǐng)求無法用實(shí)體傳參的問題

    解決Spring Cloud feign GET請(qǐng)求無法用實(shí)體傳參的問題

    這篇文章主要介紹了解決Spring Cloud feign GET請(qǐng)求無法用實(shí)體傳參的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Spring中的Lifecycle接口使用與源碼分析

    Spring中的Lifecycle接口使用與源碼分析

    這篇文章主要介紹了Spring中的Lifecycle接口使用與源碼分析,LifeCycle接口定義了Spring容器的生命周期,任何被Spring管理的對(duì)象都可以實(shí)現(xiàn)該接口,需要的朋友可以參考下
    2023-05-05
  • 使用Spring的注解方式實(shí)現(xiàn)AOP實(shí)例

    使用Spring的注解方式實(shí)現(xiàn)AOP實(shí)例

    本篇文章主要介紹了使用Spring的注解方式實(shí)現(xiàn)AOP實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06

最新評(píng)論

澄城县| 南京市| 遵义市| 五大连池市| 南安市| 玉环县| 疏附县| 灵石县| 江阴市| 正安县| 海安县| 衡南县| 芦山县| 漳州市| 乌拉特前旗| 晋中市| 项城市| 海丰县| 金昌市| 万载县| 印江| 海伦市| 武穴市| 迁西县| 台东县| 桃江县| 盐山县| 山阴县| 淮南市| 许昌市| 花莲市| 乌鲁木齐县| 松原市| 革吉县| 湖北省| 宿迁市| 白城市| 湛江市| 慈溪市| 炎陵县| 祁连县|