1. 程式人生 > >Mybatis中的反射和動態代理

Mybatis中的反射和動態代理

Mybatis的執行分為兩個部分,第一部分是讀取配置檔案快取到Configuration物件,用以建立SqlSessionFactory,第二部分是SqlSession的執行過程。相對而言,SqlSessionFactory的建立比較容易理解,而SqlSession的執行過程遠遠不是那麼簡單了,它將包括許多複雜的技術,我們需要先討論反射技術和動態代理技術,這是揭示mybatis底層架構的基礎。

反射技術:

在java中,反射技術已經大行其道,並且通過不斷優化,java的可配置性等效能得到了巨大的提升。
首先,我們來寫一個列印“hello:world”的服務:程式碼如下:

public
class Test { public void sayHello (String str) { System.err.println("hello:" + str); } }

通過反射呼叫該方法,程式碼如下:


import java.lang.reflect.Method;

public class InvokeTest {
    public static void main(String[] args) throws Exception {
        //通過反射建立test物件
        Object test = Class.forName(Test.class.getName()).newInstance();
        //獲取服務方法:sayHello
Method method = test.getClass().getMethod("sayHello", String.class); //反射呼叫方法 method.invoke(test, "world"); } }

執行main行數,結果如下:
這裡寫圖片描述

上述例子就是java反射機制的簡單應用,反射呼叫的最大好處就是配置性大大提高,就如同Spring IOC容器一樣,我們可以給很多配置引數,使得java應用程式能夠順利執行起來,大大提高java的靈活性和可配置性,降低模組之間的耦合。

JDK動態代理:

JDK的動態代理,是由JDK的java.lang.reflect.*包提供支援的,我們需要完成這麼幾個步驟:
1、 編寫服務類和介面,這個是真正的服務提供者,在JDK代理中介面是必須的
2、 編寫代理類,提供繫結和代理方法
JDK代理最大的缺點就是需要介面,而mybatis的Mapper就是一個介面,它採用的就是JDK的動態代理。我們先給一個服務介面,程式碼如下:

public interface HelloService {
    void sayHello(String str);
}

然後寫一個實現類,程式碼如下:

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String str) {
        // TODO Auto-generated method stub
        System.out.println("Hello" + str);
    }

}

現在寫一個代理類,提供真實的繫結和代理方法。代理類的要求是實現InvocationHandler介面的代理方法,當一個物件被繫結後,執行其方法的時候就會進入到代理方法裡,程式碼如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HelloServiceProxy implements InvocationHandler{

    /**
     * 真實的服務物件
     */
    private Object target;

    /**
     * 繫結一個委託物件並返回一個代理類
     * @param target
     * @return
     */
    public Object bind(Object target){
        this.target = target;
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
        return proxy;
    }

    /**
     * 通過代理物件呼叫方法首先進入這個方法
     * @param procy 代理物件
     * @param method 被呼叫方法
     * @param agrs 方法引數
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("我是JDK代理");
        Object result = null;
        //反射方法呼叫前
        System.out.println("開始呼叫方法");
        //執行方法,相當於呼叫HelloServiceImpl的sayHello方法
        result = method.invoke(target, "world");
        //反射方法呼叫後
        System.out.println("呼叫方法完成");
        return result;
    }

}

Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
這段程式碼讓JDK產生一個代理物件,這個代理物件有三個引數:第一個引數target.getClass().getClassLoader()是類載入器,第二個引數target.getClass().getInterfaces()是介面(代理物件掛載哪個介面下),第三個引數this代表當前HelloServiceProxy類,換句話說是使用HelloServiceProxy的代理方法作為物件的代理執行者。
一旦繫結後,在進入代理物件方法呼叫的時候就會到HelloServiceProxy的代理方法上,代理方法有三個引數:第一個proxy是代理物件,第二個是當前呼叫的那個方法,第三個是方法引數。比方說,現在HelloServiceImpl物件(obj)用bind方法繫結後,返回其佔位,我們在呼叫proxy.sayHelo(“hello”),那麼它就會進入到HelloServiceProxy的invoke()方法。而invoke引數中第一個便是代理物件proxy,方法是sayHello,引數是world。
讓我們測試一下動態代理,程式碼如下:

public class ProxyTest {
    public static void main(String[] args) {
        HelloServiceProxy HelloHandler = new HelloServiceProxy();
        HelloService procy = (HelloService) HelloHandler.bind(new HelloServiceImpl());
        procy.sayHello("World");
    }
}

執行結果如下:
這裡寫圖片描述

CGLIB動態代理:

JDK提供的動態代理存在一個缺陷,就是必須提供接口才可以實現,為了克服這個缺陷,我們可以採用開源框架—CGLIB,它是一種流行的動態代理。
讓我們看看如何使用CGLIB動態代理。HelloService.java和HelloServiceImpl.java不需要改變,我們只需要實現CGLIB的代理類。實現MethodInterceptor的代理方法入下:

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class HelloServiceCgLib implements MethodInterceptor{

    private Object target;
    /**
     * 建立代理物件
     * @param target
     * @return
     */
    public Object getInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        //回撥方法
        enhancer.setCallback(this);
        //建立代理物件
        return enhancer.create();
    }

    @Override
    //回撥方法
    public Object intercept(Object obj, Method methoc, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("我是cglib動態代理");
        //反射方法呼叫前
        System.out.println("開始呼叫");
        Object returnObj = proxy.invokeSuper(obj, args);
        //反射方法呼叫之後
        System.out.println("電泳完成");

        return returnObj;
    }
}

我們的呼叫形式如下:

HelloServiceCgLib hello = new HelloServiceCgLib();
HelloServiceImpl proxyImpl = (HelloServiceImpl)hello.getInstance(HelloServiceImpl.class);
proxyImpl.sayHello("hello");

這樣便能實現CGLIB的動態代理。在mybatis中通常延時載入的時候才會用到CGLIB的動態代理。
以上便是mybatis用到的代理模式,有了這些基礎就可以更好的理解mybatis的運行了。

注:以上內容根據《深入淺出mybatis技術原理與實戰》一書整理編寫,感謝作者的辛勤勞作。

相關推薦

深入淺出MyBatis反射動態代理

mybatis前三篇詳細總結了Mybatis的基本特性、常用配置、映射器,相對於Hibernate,映射器的配置相對復雜,但有很好的靈活性和擴展性,可以應對各種業務場景。熟練掌握這些內容,可以流暢的使用MyBatis進行開發了。 後面準備介紹MyBatis的解析和運行原理以及自定義插件,今天看了書籍的這部分,

Mybatis反射動態代理

Mybatis的執行分為兩個部分,第一部分是讀取配置檔案快取到Configuration物件,用以建立SqlSessionFactory,第二部分是SqlSession的執行過程。相對而言,SqlSessionFactory的建立比較容易理解,而SqlSessi

25(java反射動態代理

1 概述 反射獲取的都是class物件,以下是在不同的階段獲取物件的方式。 2 原始檔階段class物件的作用 可以利用全類名創造物件,具體程式碼為: 3 class物件獲取類中的欄位(即成員變數) 註釋:通過Class.forName()獲取到了Pe

Java高階特性—反射動態代理

1).反射   通過反射的方式可以獲取class物件中的屬性、方法、建構函式等,一下是例項: 2).動態代理   使用場景:       在之前的程式碼呼叫階段,我們用action呼叫service的方法實現業務即可。     由於之前在service中實現的業務可能不能夠滿足當先客戶的要求,需要我們重

Java核心-反射動態代理(JDK ProxyCglib)

反射和動態代理放有一定的相關性,但單純的說動態代理是由反射機制實現的,其實是不夠全面不準確的,動態代理是一種功能行為,而它的實現方法有很多。要怎麼理解以上這句話,請看下文。 一、反射 反射機制是 Java 語言提供的一種基礎功能,賦予程式在執行時 自省 (intro

Java反射動態代理

反射和動態代理放有一定的相關性,但單純的說動態代理是由反射機制實現的,其實是不夠全面不準確的,動態代理是一種功能行為,而它的實現方法有很多。要怎麼理解以上這句話,請看下文。 一、反射 反射機制是 Java 語言提供的一種基礎功能,賦予程式在執行時自省(introspect,官方用語)的能力。

Java提高班(六)反射動態代理(JDK ProxyCglib)

反射和動態代理放有一定的相關性,但單純的說動態代理是由反射機制實現的,其實是不夠全面不準確的,動態代理是一種功能行為,而它的實現方法有很多。要怎麼理解以上這句話,請看下文。 一、反射 反射機制是 Java 語言提供的一種基礎功能,賦予程式在執行時自省(introspect,官方用語)的能力。通過反射我們可

java反射動態代理實現介面記錄

專案需求:app含有廣告sdk,在上架個別應用市場時會被拒,產品希望在打包時一些渠道包把廣告sdk剝離出來(內心是哭泣的)。方法一:每次打包刪除jar包,刪除與該jar包相關的程式碼類、介面等等~記錄完成,謝謝大家哈哈 開個玩笑,言歸正傳,以上是我一開始的想法,最笨最笨的方法

面試刷題6:反射動態代理是什麼?

反射和動態代理是什麼? 反射是java提供的一種自省能力,可以在執行時建立類的例項,訪問成員變數,方法。 動態代理是程式在執行時構建代理物件動態動用方法。 反射和動態代理是第三方框架進行封裝的基礎。 反射 程式提供的一種自省能力,可以在執行時操作類和物件。 提供的核心類如下: Class: 獲取類的定義

Java反射機制動態代理

一、反射概述   反射機制指的是Java在執行時候有一種自觀的能力,能夠了解自身的情況為下一步做準備,其想表達的意思就是:在執行狀態中,對於任意一個類,都能夠獲取到這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性(包括私有的方法和屬性),這種動態獲取的資訊以及動態呼叫物件的方法的功

Java 8的靜態代理動態代理的簡單心得

歡迎來到我的第一個部落格 個人學習的一點心得,第一次寫寫的不好見諒 看看程式碼吧 程式碼1. public interface CarSales { void sell(); } 程式碼2. public class QQCar implements

java代理(靜態代理動態代理

之前本人在設計模式中有寫過靜態代理和動態代理的相關程式碼測試,可以看下。 今天我們主要學一下理論相關知識。 AOP的原理就是動態代理機制。RPC框架也是實現了AOP機制。 靜態代理 靜態代理:在程式碼編譯時就確定了被代理的類是哪一個。 這個靜態代理比較簡單,代理類和被代

反射、註解動態代理

反射是指計算機程式在執行時訪問、檢測和修改它本身狀態或行為的一種能力,是一種超程式設計語言特性,有很多語言都提供了對反射機制的支援,它使程式能夠編寫程式。Java的反射機制使得Java能夠動態的獲取類的資訊和呼叫物件的方法。 一、Java反射機制及基本用法 在Java中,Class(類型別)是反射程式設計的起

Java 反射機制動態代理是基於什麼原理,瞭解過嗎?

工作多年以及在面試中,我經常能體會到,有些面試者確實是認真努力工作,但坦白說表現出的能力水平卻不足以通過面試,通常是兩方面原因: 1、“知其然不知其所以然”。 做了多年技術,開發了很多業務應用,但似乎並未思考過種種技術選擇背後的邏輯。坦白說,我並不放心把具有一定深度的任務交給他。 2、

Android APP熱更新的外掛化(Hook技術:反射動態代理),Demo (2)

Dexposed、AndFix,(HotFix)Sophix,Qzone超級補丁的類Nuwa方式,微信的Tinker, 大眾點評的nuwa、百度金融的rocooFix, 餓了麼的amigo以及美團的robust、騰訊的Bugly熱更新。 -- 熱更新  DexPathLis

Java的靜態代理動態代理

[toc] 最近在學習MyBatis原始碼,瞭解到MyBatis裡之所以只需要開發者編寫Mapper介面即可執行SQL,就是因為JDK的動態代理在背後默默為我們做了很多事情。但是我自己對動態代理還只是一知半解,於是手機整理資料學習,整理了這篇筆記。 說到動態代理,首先要講的就是設計模式中的代理模式,而對於

MybatisjavaTypejdbcType對應關系

mat brush true real default url define red tools MyBatis 通過包含的jdbcType類型 BIT FLOAT CHAR TIMESTAMP OTHER

mybatis的#$的區別

背景 插入 trac sql註入 -m .com article 參數 -s 1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,如果傳入的值是111,那麽解析成sql時的值為order by "111", 如果傳

mybatisresultTyperesultMap的聯系

平時 sel 多對多查詢 oid resultmap key 一對一 我們 多對多 在使用mybatis進行數據庫連接操作時對於SQL語句返回結果的處理通常有兩種方式,一種就是resultType另一種就是resultMap,下面說下我對這兩者的認識和理解 比如,我們平