新聞中心
讀源碼的經(jīng)歷

十載的城關(guān)網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。網(wǎng)絡(luò)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整城關(guān)建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“城關(guān)網(wǎng)站設(shè)計(jì)”,“城關(guān)網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
剛參加工作那會(huì),沒想過去讀源碼,更沒想過去改框架的源碼;總想著別人的框架應(yīng)該是非常好的、功能齊全的,應(yīng)該不需要改;另外即使我改了源碼,怎么樣讓我的改動(dòng)生效了? 項(xiàng)目中引用的不還是沒改的jar包嗎?;叵肫饋碛X得那時(shí)候的想法確實(shí)挺......
工作了一年多之后準(zhǔn)備跳槽了,開始了一輪的面試,其中有幾個(gè)面試官就問到了相關(guān)的源碼問題:ArrayList、HashMap的底層實(shí)現(xiàn),spring、mybatis的相關(guān)源碼。問源碼的面試一般就是回去等消息,然后就沒然后了。那時(shí)候開始意識(shí)到,源碼這東西在之前的工作的中感受不到,但是在面試中好像面的還挺頻繁的,從此有意識(shí)的開始了jdk部分源碼的閱讀(主要是集合)。一開始看源碼,看的特別糙,知道個(gè)大概,知道ArrayList的底層實(shí)現(xiàn)是數(shù)組,HashMap的底層是散列表(數(shù)組+鏈表);更深入一點(diǎn)的擴(kuò)容、hash碰撞等等就不知道了。
讀spring源碼起于工作中遇到了一個(gè)問題(spring jdbcTemplate事務(wù),各種詭異,包你醍醐灌頂!),排查一段時(shí)間最終是解決了,但過程讓我非常難受,各種上網(wǎng)查資料、各種嘗試,感覺就像大海撈針一樣,遙遙無期。我下定決心,我要看一看spring的源碼,于是我買了一本《spring源碼深度解析》,結(jié)合著這本書、打開著eclipse,開始了spring的源碼閱讀之旅。至此,讀源碼成了習(xí)慣,源碼已經(jīng)進(jìn)入了我的心里。
我為什么讀源碼
很多人一定和我一樣的感受:源碼在工作中有用嗎? 用處大嗎?很長一段時(shí)間內(nèi)我也有這樣的疑問,認(rèn)為哪些有事沒事扯源碼的人就是在裝,只是為了提高他們的level而已。
那為什么我還要讀源碼呢? 一剛開始為了面試,后來為了解決工作中的問題,再后來就是個(gè)人喜好了。說的好聽點(diǎn)是有匠人精神;說的委婉點(diǎn)是好奇(底層是怎么實(shí)現(xiàn)的);說的不自信點(diǎn)是對黑盒的東西我用的沒底,怕用錯(cuò);說的簡單直白點(diǎn)是提升自我價(jià)值,為了更高的薪資待遇(這里對真正的技術(shù)迷說聲抱歉)。
源碼中我們可以學(xué)到很多東西,學(xué)習(xí)別人高效的代碼書寫、學(xué)習(xí)別人對設(shè)計(jì)模式的熟練使用、學(xué)習(xí)別人對整個(gè)架構(gòu)的布局,等等。如果你還能找出其中的不足,那么恭喜你,你要飛升了!會(huì)使用固然重要,但知道為什么這么使用同樣重要。從模仿中學(xué)習(xí),從模仿中創(chuàng)新。
讀源碼不像圍城(外面的人想進(jìn)來,里面的人想出去),它是外面的人不想進(jìn)來,里面的人不想出去;當(dāng)我們跨進(jìn)城內(nèi),你會(huì)發(fā)現(xiàn)(還是城外好,皮!)城內(nèi)風(fēng)光更好,源碼的海洋任我們遨游!
你想好入城了嗎?
我是怎么樣讀源碼的
內(nèi)容了解
首先我們要對我們的目標(biāo)有所了解,知道她有什么特點(diǎn),有些什么功能。對對方都還不了解,就想著進(jìn)入別人的內(nèi)心世界,那不是臭不要臉嘛,我們要做一個(gè)有著流氓心的紳士;對她有個(gè)大致的了解了,就可以發(fā)起攻勢,一舉拿下。
那么怎么樣了解了,方式有很多,我這里提供幾種,僅供參考
比較好的方式就是官方參考指南,親生父母往往對孩子是最了解的,對孩子的描述也是最詳細(xì)的;比如Spring Boot Reference Guide就是對springboot最詳細(xì)的描述,怎么樣使用springboot、springboot特性等等,通過此指南,springboot在你面前一覽無遺;但是,springboot畢竟是外國人的孩子,如果英語不好,估計(jì)讀起來有點(diǎn)頭疼了,不過我們有g(shù)oogle翻譯呀,咬咬牙也是能看的。源碼世界的丈母娘、老岳丈是非??犊?
其次是書籍,國外優(yōu)秀的有很多,國內(nèi)也不乏好書,比較推薦此方式,自成體系,讓我們掌握的知識(shí)點(diǎn)不至于太散。這就是好比是源碼的閨蜜,對源碼非常了解,重點(diǎn)是挺大方,會(huì)盡全力幫助我們了解源碼。
再次就是博客,雖然可能覺得知識(shí)點(diǎn)比較散,但是針對某個(gè)知識(shí)點(diǎn)卻特別的細(xì),對徹底掌握非常有幫助,園子內(nèi)就有很多技術(shù)大牛,寫的博客自然也是非常棒,非常具有學(xué)習(xí)價(jià)值。當(dāng)然還有社區(qū)、論壇、github、碼云等等。這就是源碼的朋友圈,我們從中也能獲取到非常多關(guān)于源碼的信息。
設(shè)計(jì)模式的了解
優(yōu)秀的框架、技術(shù)從不乏設(shè)計(jì)模式;jdk源碼中就應(yīng)用了很多設(shè)計(jì)模式,比如IO流中的適配器模式與裝飾模式、GUI的觀察者模式、集合中的迭代器模式等等;spring源碼中也是用到了大量的設(shè)計(jì)模式。設(shè)計(jì)模式有什么優(yōu)點(diǎn)、各適用于什么場景,不是本文的內(nèi)容,需要我們大家自行去了解。
我們只需要對一些常用的設(shè)計(jì)模式有個(gè)大致了解,再去讀源碼是比較好的;不需要將23種設(shè)計(jì)模式都通讀,也不需要將常用設(shè)計(jì)模式完全理解透;對于全部通讀,我們時(shí)間有限,另外有些模式確實(shí)不太好理解、用的少,性價(jià)比不高,沒必要全部都讀。
- 推薦書籍:《Head First Design Patterns》(中文版:《Head First 設(shè)計(jì)模式》)、《Java與模式》;
- 常用設(shè)計(jì)模式:單例模式、工廠模式、適配器模式、裝飾模式、外觀模式、代理模式、迭代器模式、觀察者模式、命令模式
另外我比較推薦的一種學(xué)習(xí)設(shè)計(jì)模式的方式是讀別人博客:java_my_life,劉偉技術(shù)博客,chenssy的設(shè)計(jì)模式;
設(shè)計(jì)模式之于源碼,就好比逛街購物之于女人,想順利勾搭源碼,我們需要好好掌握設(shè)計(jì)模式這個(gè)套路。
配合ide進(jìn)行斷點(diǎn)追蹤
我們通過源碼的圈子對源碼的了解終究只是停在表面,終究還是沒有走進(jìn)她的內(nèi)心,接下來我就和大家分享下,我是如何走進(jìn)她的內(nèi)心的!
相信看過我的源碼博客的小伙伴都知道,我非常喜歡通過idea斷點(diǎn)來進(jìn)行源碼追蹤,斷點(diǎn)追蹤源碼是我非常推薦的一種方式。斷點(diǎn)不僅可以用來調(diào)試我們的代碼,也可以用來調(diào)試我們用到的框架源碼。面對未知的、茫茫多的源碼,我們往往沒有足夠的時(shí)間、經(jīng)歷和耐心去通讀所有源碼,我們只需要去讀我們關(guān)注的部分即可(有人可能會(huì)說我都不關(guān)心,這...)。那為什么要用斷掉調(diào)試的方式來跟源碼,而不是直接從源代碼入手去跟我們關(guān)注的部分呢?嘗試過的小伙伴應(yīng)該知道,如果我們對源碼不熟悉,直接通過源碼的方式去跟,一方面很容易迷路(多態(tài),會(huì)有很多子類實(shí)現(xiàn)),不知道接下來跟哪一個(gè),另一方面也很容易跟丟,當(dāng)我們跟入的很深的時(shí)候,很有可能就忘記上一步跟到哪了。
下面我會(huì)舉例來說明我是如何進(jìn)行斷點(diǎn)追蹤的,以spring-boot-2.0.3之quartz集成,不是你想的那樣哦!和 spring-boot-2.0.3之quartz集成,數(shù)據(jù)源問題,源碼探究 為背景來講,需要搞清楚兩個(gè)點(diǎn):springboot是如何向quartz注入數(shù)據(jù)源的,quartz是如何操作數(shù)據(jù)庫的
springboot向quartz注入數(shù)據(jù)源
QuartzAutoConfiguration是springboot自動(dòng)配置quartz的入口
將quartz的配置屬性設(shè)置給SchedulerFactoryBean;將數(shù)據(jù)源設(shè)置給SchedulerFactoryBean:如果有@QuartzDataSource修飾的數(shù)據(jù)源,則將@QuartzDataSource修飾的數(shù)據(jù)源設(shè)置給SchedulerFactoryBean,否則將應(yīng)用的數(shù)據(jù)源(druid數(shù)據(jù)源)設(shè)置給SchedulerFactoryBean,顯然我們的應(yīng)用中沒有@QuartzDataSource修飾的數(shù)據(jù)源,那么SchedulerFactoryBean中的數(shù)據(jù)源就是應(yīng)用的數(shù)據(jù)源;將事務(wù)管理器設(shè)置給SchedulerFactoryBean。SchedulerFactoryBean,負(fù)責(zé)創(chuàng)建和配置quartz Scheduler,并將其注冊到spring容器中。SchedulerFactoryBean實(shí)現(xiàn)InitializingBean的afterPropertiesSet方法,里面有可以設(shè)置數(shù)據(jù)源的過程
可以看到通過org.quartz.jobStore.dataSource設(shè)置的dsName(值為quartzDs)然后會(huì)被替換成springTxDataSource.加scheduler實(shí)例名(我們的應(yīng)用中是:springTxDataSource.quartzScheduler)。springboot會(huì)注冊兩個(gè)ConnectionProvider給quartz:一個(gè)dsName叫springTxDataSource.quartzScheduler,有事務(wù);一個(gè)dsName叫springNonTxDataSource.quartzScheduler,沒事務(wù)。
quartz如何操作數(shù)據(jù)庫
我們通過停止定時(shí)任務(wù)來跟下quartz對數(shù)據(jù)庫的操作
發(fā)現(xiàn)quartz用如下方式獲取connection
- conn = DBConnectionManager.getInstance().getConnection(getDataSource());
那么我們的job中就可以按如下方式操作數(shù)據(jù)庫了
- package com.lee.quartz.job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- import org.quartz.utils.DBConnectionManager;
- import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
- import org.springframework.scheduling.quartz.QuartzJobBean;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- public class FetchDataJob extends QuartzJobBean {
- // private String dataSourceName = "quartzDs"; // 用此會(huì)找不到
- // private String dataSourceName = "springNonTxDataSource.quartzScheduler"; // 不支持事務(wù)
- // private String dataSourceName = "springTxDataSource.quartzScheduler"; // 支持事務(wù)
- private final String insertSql = "INSERT INTO tbl_sys_user(name, age) VALUES(?,?) ";
- private String schedulerInstanceName = "quartzScheduler"; // 可通過jobDataMap注入進(jìn)來
- @Override
- protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
- String dsName = LocalDataSourceJobStore.NON_TX_DATA_SOURCE_PREFIX
- + schedulerInstanceName; // 不支持事務(wù)
- //String dsName = LocalDataSourceJobStore.TX_DATA_SOURCE_PREFIX + schedulerInstanceName; // 支持事務(wù)
- try {
- Connection connection = DBConnectionManager.getInstance().getConnection(dsName);
- PreparedStatement ps = connection.prepareStatement(insertSql);
- ps.setString(1, "張三");
- ps.setInt(2, 25);
- ps.executeUpdate();
- ps.close();
- connection.close(); // 將連接歸還給連接池
- System.out.println("插入成功");
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- public void setSchedulerInstanceName(String schedulerInstanceName) {
- this.schedulerInstanceName = schedulerInstanceName;
- }
- }
明確我們的目的,找到合適的切入點(diǎn),進(jìn)入斷點(diǎn)調(diào)試追蹤也就容易了。
任我說的天花亂墜,你仍無動(dòng)于衷,那也只是我一廂情愿,只有局中人才能體會(huì)到其中的奧妙!
總結(jié)與感悟
從上至下全部通讀的方式,個(gè)人不太推薦,這是建立在很熟悉的基礎(chǔ)上的,當(dāng)我們對某個(gè)框架已經(jīng)比較熟悉了,再從上至下進(jìn)行通讀,徹底了解,這是我認(rèn)為正確的方式;但是從不熟悉到熟悉這個(gè)過程,個(gè)人不推薦全部通讀,而是推薦上面我推薦的方式 - 斷點(diǎn)局部追蹤。
很多時(shí)候,我們的博文都只是授之以魚,而我們也只是從中得到魚;而這篇的目的則是授之以漁,我希望大家從中學(xué)到捕魚的方法,而不是一味的等待別人的魚;希望大家能夠自給自足,也能把魚和漁都授予其他人。
只要我們開始去讀源碼,慢慢的就會(huì)形成自己的一套讀源碼的方式;每個(gè)人的方式都不一樣,合適自己的才是更好的。行動(dòng)起來,用合適的方式去俘獲你的的她吧!
文章標(biāo)題:雜談篇之我是怎么讀源碼的,授之以漁
網(wǎng)站地址:http://m.fisionsoft.com.cn/article/cceogoo.html


咨詢
建站咨詢
