日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

如何快速阅读源码

發(fā)布時(shí)間:2025/3/17 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何快速阅读源码 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文探討在需要了解一個(gè)開源項(xiàng)目時(shí),如何快速的理清開源項(xiàng)目的代碼邏輯!

以下是個(gè)人認(rèn)為行之有效的方法:

  • 先「跑起來」
  • 自頂向下拆解
  • 深入細(xì)節(jié)
  • 延伸改進(jìn)

本文以Mybatis為例來進(jìn)行演示!

先“跑起來”

程序界有個(gè)老傳統(tǒng),學(xué)習(xí)新技術(shù)時(shí)都是從「Hello World」開始的!無論是學(xué)習(xí)新語(yǔ)言時(shí),打印「Hello World」;還是學(xué)習(xí)新框架時(shí)編寫個(gè)demo!那為什么這里的「跑起來」要打個(gè)引號(hào)呢?

實(shí)際上,當(dāng)你想要閱讀一個(gè)開源項(xiàng)目的源碼時(shí),絕大部分情況下,你已經(jīng)能夠使用這個(gè)開源項(xiàng)目了!所以這里的“跑起來”就不是寫個(gè)「Hello World」,也不是能跑起來的程序了!而是能__在你的腦子里「跑起來」__!什么意思?

Mybatis你會(huì)用了吧?那么請(qǐng)問Mybatis是如何執(zhí)行的呢?仔細(xì)想想,你能否用完整的語(yǔ)句把它描述出來?

這里是Mybatis的官方入門文章!你是如何看這篇文章的?讀一遍就行了嗎?還是跟著文章跑一遍就夠了嗎?從這篇文章里你能獲得多少信息?

我們來理一下:

  • 安裝
    • 如何在項(xiàng)目中引入Mybatis?
    • Mybatis的groupId是什么?artifactId又是什么?目前最新版本是多少?
  • 從 XML 中構(gòu)建 SqlSessionFactory
    • SqlSessionFactoryBuilder可以通過xml或者Configuration來構(gòu)建SqlSessionFactory,那是如何構(gòu)建的呢?
    • xml配置了哪些信息?既然使用了xml,那肯定有xml解析,用什么方式解析的?
    • xml里的標(biāo)簽都是什么意思:configuration,environments,transactionManager,dataSource,mappers。以及這些標(biāo)簽的屬性分別是什么意思?
    • SqlSessionFactory的作用是什么?
  • 不使用 XML 構(gòu)建 SqlSessionFactory
    • BlogDataSourceFactory,DataSource,TransactionFactory,Environment,Configuration這些類的作用是什么?
    • *Mapper的作用是什么?
    • 為什么提供基于XML和Java的兩種配置方式?這兩種配置方式的優(yōu)缺點(diǎn)是什么?
  • 從 SqlSessionFactory 中獲取 SqlSession
    • SqlSession的作用是什么?
    • selectOne和getMapper的執(zhí)行方式有什么區(qū)別?
  • 探究已映射的 SQL 語(yǔ)句
    • *Mapper.xml的配置是什么?
    • 命名空間,id的作用是什么?
    • *Mapper.xml是如何和*Mapper.java進(jìn)行匹配的?
    • 匹配規(guī)則是什么?
    • 基于注解的映射配置如何使用?
    • 為什么提供基于XML和基于注解的兩種映射配置?有什么優(yōu)劣?
  • 作用域(Scope)和生命周期
    • SqlSessionFactoryBuilder應(yīng)該在哪個(gè)作用域使用?為什么?
    • SqlSessionFactory應(yīng)該在哪個(gè)作用域使用?為什么?
    • SqlSession應(yīng)該在哪個(gè)作用域使用?為什么?
    • Mapper實(shí)例應(yīng)該在哪個(gè)作用域使用?為什么?

回答出了上面這些問題!你也就基本能在腦子里把Mybatis「跑起來」了!之后,你才能正真的開始閱讀源碼!

當(dāng)你能把一個(gè)開源項(xiàng)目「跑起來」后,實(shí)際上你就有了對(duì)開源項(xiàng)目最初步的了解了!就像「書的索引」一樣!基于這個(gè)索引,我們一步步的進(jìn)行拆解,來細(xì)化出下一層的結(jié)構(gòu)和流程,期間可能需要深入技術(shù)細(xì)節(jié),考量實(shí)現(xiàn),考慮是否有更好的實(shí)現(xiàn)方案!也就是說后面的三步并不是線性的,而是__不斷交替執(zhí)行__的一個(gè)過程!最終就形成一個(gè)完整的源碼執(zhí)行流程!

自頂向下拆解

繼續(xù)通過Mybatis來演示(限于篇幅,我只演示一個(gè)大概流程)!我們現(xiàn)在已經(jīng)有了一個(gè)大概的流程了:

  • SqlSessionFactoryBuilder通過xml或者Configuration構(gòu)建出SqlSessionFactory
  • 可以從SqlSessionFactory中獲取SqlSession
  • SqlSession則是真正執(zhí)行sql的類

雖說每個(gè)點(diǎn)都可以往下細(xì)化,但是也分個(gè)輕重緩急!

  • 我們是先了解怎么構(gòu)建SqlSessionFactory呢?
  • 還是了解如何獲取SqlSession呢?
  • 還是了解SqlSession如何執(zhí)行sql的呢?

很明顯,SqlSession去執(zhí)行 sql才是Mybatis的核心!我們先從這個(gè)點(diǎn)入手!

首先,你當(dāng)然得先下載Mybatis的源碼了(請(qǐng)自行下載)!

我們直接去看SqlSession!它是個(gè)接口,里面有一堆執(zhí)行sql的方法!

這里只列出了一部分方法:

public interface SqlSession extends Closeable {<T> T selectOne(String statement);<E> List<E> selectList(String statement);<K, V> Map<K, V> selectMap(String statement, String mapKey);<T> Cursor<T> selectCursor(String statement);void select(String statement, Object parameter, ResultHandler handler);int insert(String statement);int update(String statement);int delete(String statement);void commit();void rollback();List<BatchResult> flushStatements();<T> T getMapper(Class<T> type);... } 復(fù)制代碼

SqlSession就是通過這些方法來執(zhí)行sql的!我們直接看我們常用的,也是Mybatis推薦的用法,就是基于Mapper的執(zhí)行!也就是說「SqlSession通過Mapper來執(zhí)行具體的sql」!上面的流程也就細(xì)化成了:

  • SqlSessionFactoryBuilder通過xml或者Configuration構(gòu)建出SqlSessionFactory
  • 可以從SqlSessionFactory中獲取SqlSession
  • SqlSession則是真正執(zhí)行sql的類
    • SqlSession獲取對(duì)應(yīng)的Mapper實(shí)例
    • Mapper實(shí)例來執(zhí)行相應(yīng)的sql

那SqlSession是如何獲取Mapper的呢?Mapper又是如何執(zhí)行sql的呢?

深入細(xì)節(jié)

我們來看SqlSession的實(shí)現(xiàn)!SqlSession有兩個(gè)實(shí)現(xiàn)類SqlSessionManager和DefaultSqlSession!通過IDE的引用功能可以查看兩個(gè)類的使用情況。你會(huì)發(fā)現(xiàn)SqlSessionManager實(shí)際并沒有使用!而DefaultSqlSession是通過DefaultSqlSessionFactory構(gòu)建的!所以我們來看DefaultSqlSession是如何構(gòu)建Mapper的!

@Override public <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this); } 復(fù)制代碼

它直接委托給了Configuration的getMapper方法!

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession); } 復(fù)制代碼

Configuration又委托給了MapperRegistry類的getMapper方法!

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type+ " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);} } 復(fù)制代碼

在MapperRegistry類的getMapper中:

  • 通過type從knownMappers中獲取對(duì)應(yīng)的MapperProxyFactory實(shí)例
  • 如果不存在則拋出異常
  • 如果存在則調(diào)用mapperProxyFactory.newInstance(sqlSession)創(chuàng)建對(duì)應(yīng)的Mapper

在這里knowMappers是什么?MapperProxyFactory又是什么?mapperProxyFactory.newInstance(sqlSession)具體做了什么?

其實(shí)很簡(jiǎn)單,knowMappers是個(gè)Map,里面包含了class與對(duì)應(yīng)的MapperProxyFactory的對(duì)應(yīng)關(guān)系!MapperProxyFactory通過newInstance來構(gòu)建對(duì)應(yīng)的Mapper(實(shí)際上是Mapper的代理)!

快接近真相了,看mapperProxyFactory.newInstance(sqlSession)里的代碼:

public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy); }protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } 復(fù)制代碼

這里干了什么?

  • 通過sqlSession,mapperInterface和methodCache構(gòu)建了一個(gè)MapperProxy對(duì)象
  • 然后通過Java的動(dòng)態(tài)代理,來生成了Mapper的代理類
  • 將Mapper方法的執(zhí)行都委托給了MapperProxy去執(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); } 復(fù)制代碼
  • 如果是Object里的方法則直接執(zhí)行
  • 否則執(zhí)行MapperMethod的execute方法
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() &&(result == null || !method.getReturnType().equals(result.getClass()))) {result = OptionalUtil.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}return result;} 復(fù)制代碼

最終實(shí)際還是委托給了sqlSession去執(zhí)行具體的sql!后面具體怎么實(shí)現(xiàn)的就自行查看吧!

延伸改進(jìn)

現(xiàn)在我們的流程大概是這樣的一個(gè)過程:

  • SqlSessionFactoryBuilder通過xml或者Configuration構(gòu)建出SqlSessionFactory
  • 可以從SqlSessionFactory中獲取SqlSession
  • SqlSession則是真正執(zhí)行sql的類
    • SqlSession獲取對(duì)應(yīng)的Mapper實(shí)例
      • DefaultSqlSession.getMapper
      • Configuration.getMapper
      • MapperRegistry.getMapper
      • mapperProxyFactory.newInstance(sqlSession)
      • 通過sqlSession,mapperInterface和methodCache構(gòu)建了一個(gè)MapperProxy對(duì)象
      • 然后通過Java的動(dòng)態(tài)代理,來生成了Mapper的代理類
    • Mapper實(shí)例來執(zhí)行相應(yīng)的sql
      • 將Mapper方法的執(zhí)行都委托給了MapperProxy去執(zhí)行
      • 如果是Object里的方法則直接執(zhí)行
      • 否則執(zhí)行MapperMethod的execute方法
      • 最終還是委托給sqlSession去執(zhí)行sql

現(xiàn)在我們大概知道了:

  • 為什么Mapper是個(gè)接口了
  • Mybatis基于這個(gè)接口做了什么

那么,

  • 什么是動(dòng)態(tài)代理(基礎(chǔ)哦)?
  • 為什么使用動(dòng)態(tài)代理來處理?
  • 基于動(dòng)態(tài)代理有什么優(yōu)點(diǎn)?又有什么缺點(diǎn)?
  • 除了動(dòng)態(tài)代理,還有其它什么實(shí)現(xiàn)方式嗎?比如說cglib?
  • 如果是其它語(yǔ)言的話,有沒有什么好的實(shí)現(xiàn)方式呢?
  • ......

這個(gè)問題列表可以很長(zhǎng),可以按個(gè)人需要去思考并嘗試回答!可能最終這些問題已經(jīng)和開源項(xiàng)目本身沒有什么關(guān)系了!但是你思考后的收獲要比看源碼本身要多得多!

再循環(huán)

一輪結(jié)束后,可以再次進(jìn)行:

  • 自頂向下拆解
  • 深入細(xì)節(jié)
  • 延伸改進(jìn)

不斷的拆解->深入->改進(jìn),最終你能__通過一個(gè)開源項(xiàng)目,學(xué)習(xí)到遠(yuǎn)比開源項(xiàng)目本身多得多的知識(shí)__!

最重要的是,你的流程是完整的。無論是最初的大致流程:

  • SqlSessionFactoryBuilder通過xml或者Configuration構(gòu)建出SqlSessionFactory
  • 可以從SqlSessionFactory中獲取SqlSession
  • SqlSession則是真正執(zhí)行sql的類

還是到最終深入的細(xì)枝末節(jié),都是個(gè)完整的流程!

這樣的好處是,你的時(shí)間能自由控制:

  • 你是要花個(gè)半天時(shí)間,了解大致流程
  • 還是花個(gè)幾天理解細(xì)節(jié)流程
  • 還是花個(gè)幾周,幾個(gè)月來深入思考,不斷延伸 你都可以從之前的流程中快速進(jìn)行下去!

而不像debug那樣的方式,需要一下子花費(fèi)很長(zhǎng)的時(shí)間去一步步的理流程,費(fèi)時(shí)費(fèi)力、收效很小,而且如果中斷了就很難繼續(xù)了!

總結(jié)

本文通過梳理Mybatis源碼的一個(gè)簡(jiǎn)單流程,來講述一個(gè)個(gè)人認(rèn)為比較好的閱讀源碼的方式,并闡述此方法與傳統(tǒng)debug方式相比的優(yōu)勢(shì)。


公眾號(hào):ivaneye

總結(jié)

以上是生活随笔為你收集整理的如何快速阅读源码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。