0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Mybatis框架和插件將動(dòng)態(tài)代理玩出了新境界

麻辣軟硬件 ? 來(lái)源:YXQ ? 2019-07-10 17:43 ? 次閱讀

靜態(tài)代理

又是一年畢業(yè)季,很多小伙伴開(kāi)始去大城市打拼。來(lái)大城市第一件事就是租房,免不了和中介打交道,因?yàn)楹芏喾繓|很忙,你根本找不到他。從這個(gè)場(chǎng)景中就可以抽象出來(lái)代理模式。

ISubject:被訪問(wèn)者資源的抽象SubjectImpl:被訪問(wèn)者具體實(shí)現(xiàn)類(lèi)(房東)SubjectProxy:被訪問(wèn)者的代理實(shí)現(xiàn)類(lèi)(中介)

UML圖如下

舉個(gè)例子來(lái)理解一下這個(gè)設(shè)計(jì)模式老板讓記錄一下用戶服務(wù)的響應(yīng)時(shí)間,用代理模式來(lái)實(shí)現(xiàn)這個(gè)功能。

public interface IUserService { public void request();}

public class UserServiceImpl implements IUserService { @Override public void request() { System.out.println(“this is userService”); }}

public class UserServiceProxy implements IUserService { private IUserService userService; public UserServiceProxy(IUserService userService) { this.userService = userService; } @Override public void request() { long startTime = System.currentTimeMillis(); userService.request(); System.out.println(“reques cost :” + (System.currentTimeMillis() - startTime)); } public static void main(String[] args) { IUserService userService = new UserServiceImpl(); UserServiceProxy userServiceProxy = new UserServiceProxy(userService); // this is userService // reques cost :0 userServiceProxy.request(); }}

一切看起來(lái)都非常的美好,老板又發(fā)話了,把產(chǎn)品服務(wù)的響應(yīng)時(shí)間也記錄一下吧。又得寫(xiě)如下3個(gè)類(lèi)

IProductService ProductServiceImpl ProductServiceProxy

UserServiceProxy和ProductServiceProxy這兩個(gè)代理類(lèi)的邏輯都差不多,卻還得寫(xiě)2次。其實(shí)這個(gè)還好,如果老板說(shuō),把現(xiàn)有系統(tǒng)的幾十個(gè)服務(wù)的響應(yīng)時(shí)間都記錄一下吧,你是不是要瘋了?這得寫(xiě)多少代理類(lèi)???

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

黑暗總是暫時(shí)的,終究會(huì)迎來(lái)黎明,在JDK1.3之后引入了一種稱(chēng)之為動(dòng)態(tài)代理(Dynamic Proxy)的機(jī)制。使用該機(jī)制,我們可以為指定的接口在系統(tǒng)運(yùn)行期間動(dòng)態(tài)地生成代理對(duì)象,從而幫助我們走出最初使用靜態(tài)代理實(shí)現(xiàn)AOP的窘境

動(dòng)態(tài)代理的實(shí)現(xiàn)主要由一個(gè)類(lèi)和一個(gè)接口組成,即java.lang.reflect.Proxy類(lèi)和java.lang.reflect.InvocationHandler接口。

讓我們用動(dòng)態(tài)代理來(lái)改造一下上面記錄系統(tǒng)響應(yīng)時(shí)間的功能。雖然要為IUserService和IProductService兩種服務(wù)提供代理對(duì)象,但因?yàn)榇韺?duì)象中要添加的橫切邏輯是一樣的。所以我們只需要實(shí)現(xiàn)一個(gè)InvocationHandler就可以了。代碼如下

public class RequestCostInvocationHandler implements InvocationHandler { private Object target; public RequestCostInvocationHandler(Object target) { this.target = target; } /** 被代理對(duì)象的任何方法被執(zhí)行時(shí),都會(huì)先進(jìn)入這個(gè)方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals(“request”)) { long startTime = System.currentTimeMillis(); // 執(zhí)行目標(biāo)對(duì)象的方法 method.invoke(target, args); System.out.println(“reques cost :” + (System.currentTimeMillis() - startTime)); } return null; } public static void main(String[] args) { // 3個(gè)參數(shù)解釋如下 // classloader,生成代理類(lèi) // 代理類(lèi)應(yīng)該實(shí)現(xiàn)的接口 // 實(shí)現(xiàn)InvocationHandler的切面類(lèi) IUserService userService = (IUserService) Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, new RequestCostInvocationHandler(new UserServiceImpl())); IProductService productService = (IProductService) Proxy.newProxyInstance(IProductService.class.getClassLoader(), new Class[]{IProductService.class}, new RequestCostInvocationHandler(new ProductServiceImpl())); // this is userService // reques cost :0 userService.request(); // this is productService // reques cost :0 productService.request(); }}

UML圖如下。恭喜你,你現(xiàn)在已經(jīng)理解了Spring AOP是怎么回事了,就是這么簡(jiǎn)單,今天先不展開(kāi)談Spring

先簡(jiǎn)單談?wù)剟?dòng)態(tài)代理在Mybatis中是如何被大佬玩的出神入化的

Mybatis核心設(shè)計(jì)思路

相信用過(guò)mybatis的小伙伴都能理解下面這段代碼,通過(guò)roleMapper這個(gè)接口直接從數(shù)據(jù)庫(kù)中拿到一個(gè)對(duì)象

Role role = roleMapper.getRole(3L);

直覺(jué)告訴我,一個(gè)接口是不能運(yùn)行的啊,一定有接口的實(shí)現(xiàn)類(lèi),可是這個(gè)實(shí)現(xiàn)類(lèi)我自己沒(méi)寫(xiě)啊,難道m(xù)ybatis幫我們生成了?你猜的沒(méi)錯(cuò),mybatis利用動(dòng)態(tài)代理幫我們生成了接口的實(shí)現(xiàn)類(lèi),這個(gè)類(lèi)就是org.apache.ibatis.binding.MapperProxy,我先畫(huà)一下UML圖,MapperProxy就是下圖中的SubjectProxy類(lèi)

和上面的UML類(lèi)圖對(duì)比一下,發(fā)現(xiàn)不就少了一個(gè)SubjectImpl類(lèi)嗎?那應(yīng)該就是SubjectProxy類(lèi)把SubjectImple類(lèi)要做的事情做了唄,猜對(duì)了。SubjectProxy通過(guò)SubjectImple和SubjectImple.xml之間的映射關(guān)系知道自己應(yīng)該執(zhí)行什么SQL。所以mybatis最核心的思路就是這么個(gè)意思,細(xì)節(jié)之類(lèi)的可以看源碼,理清最主要的思路,看源碼就能把握住重點(diǎn)。

Mybatis插件原理

mybatis的插件也用到了動(dòng)態(tài)代理,還用到了責(zé)任鏈模式,我就不從源碼角度分析了。說(shuō)一下大概實(shí)現(xiàn),我們用插件肯定是為了在原先的基礎(chǔ)上增加新功能。增加一個(gè)插件,mybatis就在原先類(lèi)的基礎(chǔ)上用動(dòng)態(tài)代理生成一個(gè)代理對(duì)象,如果有多個(gè)插件,就在代理對(duì)象的基礎(chǔ)上再生成代理對(duì)象,形式和如下函數(shù)差不多

plugin2( plugin1( start() ) )

我再給你寫(xiě)個(gè)例子,你再看看相關(guān)的源碼分析文章(也許我以后會(huì)寫(xiě)),很快就能理解了。

在mybatis中要想用插件,有如下2個(gè)步驟1.在mybatis-config.xml中配置插件,如下所示

《plugins》 《plugin interceptor=“org.xrq.mybatis.plugin.FirstInterceptor” /》 《plugin interceptor=“org.xrq.mybatis.plugin.SecondInterceptor” /》《/plugins》

2.插件類(lèi)還得實(shí)現(xiàn)Interceptor接口

我現(xiàn)在給一個(gè)需求,一個(gè)應(yīng)用返回字符串0,我加一個(gè)插件在字符串的左右兩邊加plugin1,再加一個(gè)插件在字符串的左右兩邊加plugin2,開(kāi)寫(xiě)

返回字符串的接口

public interface IGetStr { public String getStrZero(); public String getStrOne();}

返回字符串的實(shí)現(xiàn)類(lèi)

public class GetStrImpl implements IGetStr { @Override public String getStrZero() { return “0”; } @Override public String getStrOne() { return “1”; }}

定義攔截器接口

public interface Interceptor { /** 執(zhí)行攔截邏輯的方法 */ Object intercept(Invocation invocation); /** * target是被攔截的對(duì)象,它的作用是給被攔截對(duì)象生成一個(gè)代理對(duì)象,并返回它。 * 為了方便,可以直接使用Mybatis中org.apache.ibatis.plugin類(lèi)的wrap方法生成代理對(duì)象 * 我這里也寫(xiě)了一個(gè)Plugin方法 */ Object plugin(Object target);}

看到一個(gè)不認(rèn)識(shí)的類(lèi)Invocation,定義如下

public class Invocation { private final Object target; private final Method method; private final Object[] args; public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); }}

就是簡(jiǎn)單的封裝了一下目標(biāo)對(duì)象,目標(biāo)方法和目標(biāo)方法的參數(shù)。proceed方法就是執(zhí)行目標(biāo)對(duì)象的目標(biāo)方法

Plugin算是一個(gè)工具類(lèi),生成代理對(duì)象

public class Plugin implements InvocationHandler { /** 目標(biāo)對(duì)象 */ private final Object target; /** Interceptor對(duì)象 */ private final Interceptor interceptor; public Plugin(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } /** 生成代理對(duì)象 */ public static Object wrap(Object target, Interceptor interceptor) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[]{IGetStr.class}, new Plugin(target, interceptor)); } /** 被代理對(duì)象的方法執(zhí)行時(shí),這個(gè)方法會(huì)被執(zhí)行 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 只為方法getStrZero生成代理對(duì)象 if (method.getName().equals(“getStrZero”)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); }}

寫(xiě)第一個(gè)插件

public class FirstInterceptor implements Interceptor { /** 執(zhí)行攔截邏輯的方法 */ @Override public Object intercept(Invocation invocation) { try { return “plugin1 ” + invocation.proceed() + “ plugin1”; } catch (Exception e) { return null; } } /** 為原先的類(lèi)生成代理對(duì)象 */ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); }}

有2個(gè)方法

plugin是為插件生成代理對(duì)象,用了我自己寫(xiě)的Plugin工具類(lèi)intercept是增加攔截邏輯,invocation.proceed()是執(zhí)行目標(biāo)對(duì)象的目標(biāo)方法,前文說(shuō)過(guò)了哈,這里我們只對(duì)輸出做了改變

第二個(gè)插件和第一個(gè)插件類(lèi)似

public class SecondInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) { try { return “plugin2 ” + invocation.proceed() + “ plugin2”; } catch (Exception e) { return null; } } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); }}

用一個(gè)容器保存插件,這里用到了責(zé)任鏈模式

public class InterceptorChain { /** 放攔截器 */ private final List《Interceptor》 interceptors = new ArrayList《Interceptor》(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); }}

pluginAll方法是精髓,為每個(gè)插件一層一層的生成代理對(duì)象,就像套娃娃一樣。

驗(yàn)證一下

public class Main { public static void main(String[] args) { // 配置插件 InterceptorChain interceptorChain = new InterceptorChain(); interceptorChain.addInterceptor(new FirstInterceptor()); interceptorChain.addInterceptor(new SecondInterceptor()); // 獲得代理對(duì)象 IGetStr getStr = new GetStrImpl(); getStr = (IGetStr) interceptorChain.pluginAll(getStr); String result = getStr.getStrZero(); // plugin2 plugin1 0 plugin1 plugin2 System.out.println(result); result = getStr.getStrOne(); // 1 System.out.println(result); }}

大功告成,可以看到先定義的插件先執(zhí)行。

類(lèi)有點(diǎn)多,如果看的有點(diǎn)暈,多看幾次,你就很容易理解了,我這里還是精簡(jiǎn)了很多。

一個(gè)InvocationHandler接口被大佬玩出了新境界,果然編程這件事還得靠想象力。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3521

    瀏覽量

    93268

原文標(biāo)題:Mybatis框架和插件將動(dòng)態(tài)代理玩出了新境界

文章出處:【微信號(hào):VOSDeveloper,微信公眾號(hào):麻辣軟硬件】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    JDK動(dòng)態(tài)代理的原理

    在Java中,動(dòng)態(tài)代理是一種機(jī)制,允許在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建代理對(duì)象來(lái)代替某個(gè)實(shí)際對(duì)象,從而在其前后執(zhí)行額外的邏輯。 為什么JDK動(dòng)態(tài)
    的頭像 發(fā)表于 09-30 10:51 ?468次閱讀

    數(shù)據(jù)庫(kù)整合Mybatis框架

    微服務(wù) SpringBoot 20(九):整合Mybatis
    發(fā)表于 07-16 11:03

    請(qǐng)問(wèn)mybatis分頁(yè)插件有哪些使用案列?

    mybatis分頁(yè)插件使用案例
    發(fā)表于 11-09 06:21

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

    定義:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 動(dòng)態(tài)代理使用 java動(dòng)態(tài)代理機(jī)制以巧妙的方式實(shí)現(xiàn)了
    發(fā)表于 09-27 15:14 ?0次下載

    mybatis是什么_MyBatis的優(yōu)缺點(diǎn)詳解_mybatis框架入門(mén)詳解

    Mybatis框架是別人開(kāi)發(fā)的一種半成品軟件,可以用來(lái)通過(guò)定制輔助快速開(kāi)發(fā)是工具。MyBatis應(yīng)用程序根據(jù)XML配置文件創(chuàng)建SqlSessionFactory,SqlSessionFactory在
    發(fā)表于 02-24 09:16 ?2w次閱讀

    mybatis動(dòng)態(tài)sql詳解

    本文詳細(xì)介紹了mybatis執(zhí)行動(dòng)態(tài)sql語(yǔ)句的方法。
    發(fā)表于 02-24 11:37 ?3778次閱讀

    easy-mybatis Mybatis的增強(qiáng)框架

    ./oschina_soft/gitee-easy-mybatis.zip
    發(fā)表于 06-14 09:45 ?1次下載
    easy-<b class='flag-5'>mybatis</b> <b class='flag-5'>Mybatis</b>的增強(qiáng)<b class='flag-5'>框架</b>

    Fluent Mybatis、原生MybatisMybatis Plus對(duì)比

    mapper中再組裝參數(shù)。那對(duì)比原生Mybatis, Mybatis Plus或者其他框架,F(xiàn)luentMybatis提供了哪些便利呢?
    的頭像 發(fā)表于 09-15 15:41 ?1333次閱讀

    一文掌握MyBatis動(dòng)態(tài)SQL使用與原理

    摘要:使用動(dòng)態(tài) SQL 并非一件易事,但借助可用于任何 SQL 映射語(yǔ)句中的強(qiáng)大的動(dòng)態(tài) SQL 語(yǔ)言,MyBatis 顯著地提升了這一特性的易用性。
    的頭像 發(fā)表于 01-06 11:27 ?850次閱讀

    MyBatis-Plus為什么不支持聯(lián)表

    MyBatis Plus Join`一款專(zhuān)門(mén)解決MyBatis Plus 關(guān)聯(lián)查詢(xún)問(wèn)題的擴(kuò)展框架,他并不一款全新的框架,而是基于`MyBatis
    的頭像 發(fā)表于 02-28 15:19 ?2233次閱讀
    <b class='flag-5'>MyBatis</b>-Plus為什么不支持聯(lián)表

    Rust構(gòu)建QEMU插件框架

    Cannonball 是一個(gè)用 Rust 構(gòu)建 QEMU 插件框架!您可以在 C 語(yǔ)言的 QEMU TCG 插件中執(zhí)行的任何操作,都可以使用cannonball。編寫(xiě)以最小的開(kāi)銷(xiāo)和盡可能多的功能運(yùn)行的
    的頭像 發(fā)表于 07-21 16:57 ?758次閱讀

    MyBatis動(dòng)態(tài)sql是什么?MyBatis動(dòng)態(tài)SQL最全教程

    動(dòng)態(tài) SQL 是 MyBatis 的強(qiáng)大特性之一。在 JDBC 或其它類(lèi)似的框架中,開(kāi)發(fā)人員通常需要手動(dòng)拼接 SQL 語(yǔ)句。根據(jù)不同的條件拼接 SQL 語(yǔ)句是一件極其痛苦的工作。
    的頭像 發(fā)表于 08-10 10:18 ?811次閱讀

    mybatis接口動(dòng)態(tài)代理原理

    MyBatis是一款輕量級(jí)的Java持久化框架,它通過(guò)XML或注解配置的方式,數(shù)據(jù)庫(kù)操作與SQL語(yǔ)句解耦,提供了一種簡(jiǎn)單、靈活的數(shù)據(jù)訪問(wèn)方式。在MyBatis中,使用
    的頭像 發(fā)表于 12-03 11:52 ?748次閱讀

    mybatis和mybatisplus的區(qū)別

    MyBatisMyBatis Plus是兩個(gè)非常受歡迎的Java持久層框架。這兩個(gè)框架在設(shè)計(jì)和功能上有一些區(qū)別,下面我詳細(xì)介紹它們之間的
    的頭像 發(fā)表于 12-03 11:53 ?2234次閱讀

    mybatis框架的主要作用

    MyBatis框架是一種流行的Java持久化框架,主要用于簡(jiǎn)化數(shù)據(jù)庫(kù)操作和管理。它提供了一種簡(jiǎn)潔的方式來(lái)訪問(wèn)數(shù)據(jù)庫(kù),并將SQL語(yǔ)句從Java代碼中分離出來(lái),從而提高了代碼的可維護(hù)性和可讀性
    的頭像 發(fā)表于 12-03 14:49 ?1816次閱讀