亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

分析mybatis運(yùn)行原理

 更新時(shí)間:2021年06月18日 17:26:37   作者:煙花散盡13141  
Mybatis是一個(gè)優(yōu)秀的持久層框架,它對(duì)JDBC操作數(shù)據(jù)庫(kù)的過(guò)程進(jìn)行封裝,使開(kāi)發(fā)者只需要關(guān)注sql本身。我們?cè)瓉?lái)使用JDBC操作數(shù)據(jù)庫(kù),需要手動(dòng)的寫(xiě)代碼去注冊(cè)驅(qū)動(dòng)、獲取connection、獲取statement等等,現(xiàn)在Mybaits幫助我們把這些事情做了,我們只需要關(guān)注我們的業(yè)務(wù)sql即可

一、Mybatis基本認(rèn)識(shí)

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

  • 之前我們知道Mapper僅僅是一個(gè)接口,而不是一個(gè)邏輯實(shí)現(xiàn)類。但是在Java中接口是無(wú)法執(zhí)行邏輯的。這里Mybatis就是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的。關(guān)于動(dòng)態(tài)代理我們常用的有Jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理。兩種卻別這里不做贅述。關(guān)于CGLIB代理在框架中使用的比較多。
  • 關(guān)于動(dòng)態(tài)代理就是所有的請(qǐng)求有一個(gè)入口,由這個(gè)入口進(jìn)行分發(fā)。在開(kāi)發(fā)領(lǐng)域的一個(gè)用途就是【負(fù)載均衡】
  • 關(guān)于Mybatis的動(dòng)態(tài)代理是使用了兩種的結(jié)合。
  • 下面看看JDK和cglib兩種實(shí)現(xiàn)

JDK實(shí)現(xiàn)首先我們需要提供一個(gè)接口 , 這個(gè)接口是對(duì)我們程序員的一個(gè)抽象。 擁有編碼和改BUG的本領(lǐng)

public interface Developer {

    /**
     * 編碼
     */
    void code();

    /**
     * 解決問(wèn)題
     */
    void debug();
}

關(guān)于這兩種本領(lǐng)每個(gè)人處理方式不同。這里我們需要一個(gè)具體的實(shí)例對(duì)象

public class JavaDeveloper implements Developer {
    @Override
    public void code() {
        System.out.println("java code");
    }

    @Override
    public void debug() {
        System.out.println("java debug");
    }
}

我們傳統(tǒng)的調(diào)用方式是通過(guò)java提供的new 機(jī)制創(chuàng)造一個(gè)JavaDeveloper對(duì)象出來(lái)。而通過(guò)動(dòng)態(tài)代理是通過(guò)java.lang.reflect.Proxy對(duì)象創(chuàng)建對(duì)象調(diào)用實(shí)際方法的。

通過(guò)newProxyInstance方法獲取接口對(duì)象的。而這個(gè)方法需要三個(gè)參數(shù)

  • ClassLoader loader : 通過(guò)實(shí)際接口實(shí)例對(duì)象獲取ClassLoader
  • Class<?>[] interfaces : 我們抽象的接口
  • InvocationHandler h : 對(duì)我們接口對(duì)象方法的調(diào)用。在調(diào)用節(jié)點(diǎn)我們可以進(jìn)行我們的業(yè)務(wù)攔截
JavaDeveloper jDeveloper = new JavaDeveloper();
Developer developer = (Developer) Proxy.newProxyInstance(jDeveloper.getClass().getClassLoader(), jDeveloper.getClass().getInterfaces(), (proxy, method, params) -> {
    if (method.getName().equals("code")) {
        System.out.println("我是一個(gè)特殊的人,code之前先分析問(wèn)題");
        return method.invoke(jDeveloper, params);
    }
    if (method.getName().equals("debug")) {
        System.out.println("我沒(méi)有bug");

    }
    return null;
});
developer.code();
developer.debug();

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

cglib動(dòng)態(tài)代理優(yōu)點(diǎn)在于他不需要我們提前準(zhǔn)備接口。他代理的實(shí)際的對(duì)象。這對(duì)于我們開(kāi)發(fā)來(lái)說(shuō)就很方便了。

public class HelloService {
    public HelloService() {
        System.out.println("HelloService構(gòu)造");
    }

    final public String sayHello(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }

    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

下面我們只需要實(shí)現(xiàn)cglib提供的MethodInterceptor接口,在初始化設(shè)置cglib的時(shí)候加載這個(gè)實(shí)例化對(duì)象就可以了

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}

下面我們就來(lái)初始化設(shè)置cglib

public static void main(String[] args) {
    //代理類class文件存入本地磁盤(pán)方便我們反編譯查看源代碼
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/root/code");
    //通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象過(guò)程
    Enhancer enhancer = new Enhancer();
    //設(shè)置enhancer對(duì)象的父類
    enhancer.setSuperclass(HelloService.class);
    // 設(shè)置enhancer的回調(diào)對(duì)象
    enhancer.setCallback(new MyMethodInterceptor());
    //創(chuàng)建代理對(duì)象
    HelloService helloService = (HelloService) enhancer.create();
    //通過(guò)代理對(duì)象調(diào)用目標(biāo)方法
    helloService.sayHello();
}

仔細(xì)看看cglib和spring的aop特別像。針對(duì)切點(diǎn)進(jìn)行切面攔截控制。

總結(jié):

通過(guò)對(duì)比兩種動(dòng)態(tài)代理我們很容易發(fā)現(xiàn),mybatis就是通過(guò)JDK代理實(shí)現(xiàn)Mapper調(diào)用的。我們Mapper接口實(shí)現(xiàn)通過(guò)代理到xml中對(duì)應(yīng)的sql執(zhí)行邏輯

1.2、反射

  • 相信有一定經(jīng)驗(yàn)的Java工程師都對(duì)反射或多或少有一定了解。其實(shí)從思想上看不慣哪種語(yǔ)言都是有反射的機(jī)制的。
  • 通過(guò)反射我們就擺脫了對(duì)象的限制我們調(diào)用方法不再需要通過(guò)對(duì)象調(diào)用了。可以通過(guò)Class對(duì)象獲取方法對(duì)象。從而通過(guò)invoke方法進(jìn)行方法的調(diào)用了。

二、Configuration對(duì)象作用

Configuration對(duì)象存儲(chǔ)了所有Mybatis的配置。主要初始化一下參數(shù)

  • properties
  • settings
  • typeAliases
  • typeHandler
  • ObjectFactory
  • plugins
  • environment
  • DatabaseIdProvider
  • Mapper映射器

三、映射器結(jié)構(gòu)

  • BoundSql提供三個(gè)主要的屬性 parameterMappings 、parameterObject、sql
  • parameterObject參數(shù)本身。我們可以傳遞java基本類型、POJO、Map或者@Param標(biāo)注的參數(shù)。
  • 當(dāng)我們傳遞的是java基本類型mybatis會(huì)轉(zhuǎn)換成對(duì)應(yīng)的包裝對(duì)象 int -> Integer
  • 如果我們傳遞POJO、Map。就是對(duì)象本身
  • 我們傳遞多個(gè)參數(shù)且沒(méi)有@Param指定變量名則parameterObject 類似
  • {"1":p1,"2":p2,"param1":p1,"param2":p2}
  • 我們傳遞多個(gè)參數(shù)且@Param指定變量名 則parameterObject類似
  • {"key1":p1,"key2":p2,"param1":p1,"param2":p2}
  • parameterMapping 是記錄屬性、名稱、表達(dá)式、javaType,jdbcType、typeHandler這些信息
  • sql 屬性就是我們映射器中的一條sql. 正常我們?cè)诔R?jiàn)中對(duì)sql進(jìn)行校驗(yàn)。正常不需要修改sql。

四、sqlsession執(zhí)行流程(源碼跟蹤)

首先我們看看我們平時(shí)開(kāi)發(fā)的Mapper接口是如何動(dòng)態(tài)代理的。這就需要提到MapperProxyFactory這個(gè)類了。該類中的newInstance方法

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

通過(guò)上滿代碼及上述對(duì)jdk動(dòng)態(tài)代理的表述。我們可以知道m(xù)apperProxy是我們代理的重點(diǎn)。MapperProxy是InvocationHandler的實(shí)現(xiàn)類。他重寫(xiě)的invoke方法就是代理對(duì)象執(zhí)行的方法入口。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
    if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
    }
} catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
    & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
    && method.getDeclaringClass().isInterface();
}

通過(guò)源碼發(fā)現(xiàn)。invoke內(nèi)部首先判斷對(duì)象是否是類 。 通過(guò)打斷點(diǎn)發(fā)現(xiàn)最終會(huì)走到cacheMapperMethod這個(gè)方法去創(chuàng)建MapperMethod對(duì)象。繼續(xù)查看MapperMethod中execute方法我們可以了解到內(nèi)部實(shí)現(xiàn)其實(shí)是一個(gè)命令行模式開(kāi)發(fā)。通過(guò)判斷命令從而執(zhí)行不同的語(yǔ)句。判斷到具體執(zhí)行語(yǔ)句然后將參數(shù)傳遞給sqlsession進(jìn)行sql調(diào)用并獲取結(jié)果。到了sqlsession就和正常jdbc開(kāi)發(fā)sql進(jìn)行關(guān)聯(lián)了。sqlsession中Executor、StatementHandler、ParameterHandlerResulthandler四大天王

4.1、Executor

顧名思義他就是一個(gè)執(zhí)行器。將java提供的sql提交到數(shù)據(jù)庫(kù)。Mybatis提供了三種執(zhí)行器。

Configuration.classnewExecutor源碼

根據(jù)uml我們不難看出mybatis中提供了三類執(zhí)行器分別SimpleExecutor、ReuseExecutor、BatchExecutor

public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 得到configuration 中的environment
      final Environment environment = configuration.getEnvironment();
      // 得到configuration 中的事務(wù)工廠
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 獲取執(zhí)行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回默認(rèn)的SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

通過(guò)上述源碼我們知道在sqlsession獲取一個(gè)數(shù)據(jù)庫(kù)session對(duì)象時(shí)我們或根據(jù)我們的settings配置加載一個(gè)Executor對(duì)象。在settings中配置也很簡(jiǎn)單

<settings>
<!--取值范圍 SIMPLE, REUSE, BATCH -->
	<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

我們也可以通過(guò)java代碼設(shè)置

factory.openSession(ExecutorType.BATCH);

4.2、StatementHandler

顧名思義,StatementHandler就是專門處理數(shù)據(jù)庫(kù)回話的。這個(gè)對(duì)象的創(chuàng)建還是在Configuration中管理的。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

很明顯Mybatis中StatementHandler使用的是RoutingStatementHandler這個(gè)class

關(guān)于StatementHandler和RoutingStatementHandler之間的關(guān)系我們通過(guò)源碼可以看出這里和Executor一樣都是適配器模式。采用這種模式的好處是方便我們對(duì)這些對(duì)象進(jìn)行代理。這里讀者可以猜測(cè)一下是使用了哪種動(dòng)態(tài)代理。給點(diǎn)提示 這里使用了接口哦


在查看BaseStatementHandler結(jié)構(gòu)我們會(huì)發(fā)現(xiàn)和Executor一模一樣。同樣的Mybatis在構(gòu)造RoutingStatementHandler的時(shí)候會(huì)根據(jù)setting中配置來(lái)加載不同的具體子類。這些子類都是繼承了BaseStatementHandler.

前一節(jié)我們跟蹤了Executor。 我們知道Mybatis默認(rèn)的是SimpleExecutor。 StatementHandler我們跟蹤了Mybaits默認(rèn)的是PrePareStatementHandler。在SimpleExecutor執(zhí)行查詢的源碼如下


我們發(fā)現(xiàn)在executor查詢錢會(huì)先讓statementHandler構(gòu)建一個(gè)Statement對(duì)象。最終就是StatementHandler中prepare方法。這個(gè)方法在抽象類BaseStatmentHandler中已經(jīng)封裝好了。

這個(gè)方法的邏輯是初始化statement和設(shè)置連接超時(shí)等一些輔助作用

然后就是設(shè)置一些參數(shù)等設(shè)置。最后就走到了執(zhí)行器executor的doquery

PrepareStatement在我們jdbc開(kāi)發(fā)時(shí)是常見(jiàn)的一個(gè)類 。 這個(gè)方法執(zhí)行execute前我們需要設(shè)置sql語(yǔ)句,設(shè)置參數(shù)進(jìn)行編譯。這一系列步驟就是剛才我們說(shuō)的流程也是PrepareStatementHandler.prepareStatement幫我們做的事情。那么剩下的我們也很容易想到就是我們對(duì)數(shù)據(jù)結(jié)果的封裝。正如代碼所示下馬就是resultSetHandler幫我們做事情了。

4.3、結(jié)果處理器(ResultSetHandler)

@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

這個(gè)方法我們可以導(dǎo)出來(lái)是結(jié)果xml中標(biāo)簽配置對(duì)結(jié)果的一個(gè)封裝。

4.4、總結(jié)

SqlSession在一個(gè)查詢開(kāi)啟的時(shí)候會(huì)先通過(guò)CacheExecutor查詢緩存。擊穿緩存后會(huì)通過(guò)BaseExector子類的SimpleExecutor創(chuàng)建StatementHandler。PrepareStatementHandler會(huì)基于PrepareStament執(zhí)行數(shù)據(jù)庫(kù)操作。并針對(duì)返回結(jié)果通過(guò)ResultSetHandler返回結(jié)果數(shù)據(jù)

以上就是分析mybatis運(yùn)行原理的詳細(xì)內(nèi)容,更多關(guān)于mybatis運(yùn)行原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Security跳轉(zhuǎn)頁(yè)面失敗問(wèn)題解決

    Spring Security跳轉(zhuǎn)頁(yè)面失敗問(wèn)題解決

    這篇文章主要介紹了Spring Security跳轉(zhuǎn)頁(yè)面失敗問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • java線程之死鎖

    java線程之死鎖

    這篇文章主要介紹了Java線程之死鎖,死鎖是這樣一種情形-多個(gè)線程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放。由于線程被無(wú)限期地阻塞,因此程序不可能正常終止
    2022-05-05
  • Java實(shí)現(xiàn)List反轉(zhuǎn)的方法總結(jié)

    Java實(shí)現(xiàn)List反轉(zhuǎn)的方法總結(jié)

    在Java中,反轉(zhuǎn)一個(gè)List意味著將其元素的順序顛倒,使得第一個(gè)元素變成最后一個(gè),最后一個(gè)元素變成第一個(gè),依此類推,這一操作在處理數(shù)據(jù)集合時(shí)非常有用,所以本文給大家總結(jié)了Java實(shí)現(xiàn)List反轉(zhuǎn)的方法,需要的朋友可以參考下
    2024-04-04
  • Java SpringBoot模板引擎之 Thymeleaf入門詳解

    Java SpringBoot模板引擎之 Thymeleaf入門詳解

    jsp有著強(qiáng)大的功能,能查出一些數(shù)據(jù)轉(zhuǎn)發(fā)到JSP頁(yè)面以后,我們可以用jsp輕松實(shí)現(xiàn)數(shù)據(jù)的顯示及交互等,包括能寫(xiě)Java代碼。但是,SpringBoot首先是以jar的方式,不是war;其次我們的tomcat是嵌入式的,所以現(xiàn)在默認(rèn)不支持jsp
    2021-10-10
  • Java SiteMesh新手學(xué)習(xí)教程代碼案例

    Java SiteMesh新手學(xué)習(xí)教程代碼案例

    這篇文章主要介紹了Java SiteMesh新手學(xué)習(xí)教程代碼案例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)url攔截(基于rbac模型)

    SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)url攔截(基于rbac模型)

    本文主要介紹了SpringSecurity動(dòng)態(tài)url攔截,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 利用Java實(shí)現(xiàn)更改Word中的頁(yè)面大小和頁(yè)面方向

    利用Java實(shí)現(xiàn)更改Word中的頁(yè)面大小和頁(yè)面方向

    這篇文章主要為大家詳細(xì)介紹了一種高效便捷的方法——通過(guò)Java應(yīng)用程序,以編程方式更改Word中的頁(yè)面大小和頁(yè)面方向,感興趣的可以了解一下
    2023-03-03
  • Springboot集成fastDFS配置過(guò)程解析

    Springboot集成fastDFS配置過(guò)程解析

    這篇文章主要介紹了Springboot集成fastDFS配置過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 手寫(xiě)mybatis完整sql插件問(wèn)題及實(shí)現(xiàn)思路

    手寫(xiě)mybatis完整sql插件問(wèn)題及實(shí)現(xiàn)思路

    大家在使用mybatis的過(guò)程中,mysql日志功能一般不會(huì)直接放到數(shù)據(jù)庫(kù)中執(zhí)行的,今天小編重點(diǎn)給大家分享手寫(xiě)mybatis完整sql插件問(wèn)題及實(shí)現(xiàn)思路,對(duì)mybatis完整sql插件相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-05-05
  • 關(guān)于feign對(duì)x-www-form-urlencode類型的encode和decode問(wèn)題

    關(guān)于feign對(duì)x-www-form-urlencode類型的encode和decode問(wèn)題

    這篇文章主要介紹了關(guān)于feign對(duì)x-www-form-urlencode類型的encode和decode問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評(píng)論