雜談篇之我是怎麼讀源碼的,授之以漁

前言

  開心一刻html

    今天上課不當心睡着了,結果被老師叫起來回答問題,這是背景。無奈之下看向同桌尋求幫助,同桌小聲說到選C,結果周圍的人都說選C,向同桌投去一個感激的眼神後大聲說道選C。剛說完教室就笑開了,老師一臉恨鐵不成鋼的表情說選你個頭,我叫你翻譯文言文你選C!你出去,你給我出去。看着同桌擠眉弄眼的表情,勞資真想說,這幫畜生java

互相抱怨道:你是否是又長胖了?git

  路漫漫其修遠兮,吾將上下而求索!github

  github:https://github.com/youzhibing面試

  碼雲(gitee):https://gitee.com/youzhibingspring

讀源碼的經歷

  剛參加工做那會,沒想過去讀源碼,更沒想過去改框架的源碼;總想着別人的框架應該是完美的、萬能的,應該不須要改;另外即便我改了源碼,怎麼樣讓個人改動生效了? 項目中引用的不仍是沒改的jar包嗎。回想起來以爲那時候的想法確實挺......sql

  工做了一年多以後準備跳槽了,開始了一輪的面試,其中有幾個面試官就問到了相關的源碼問題:ArrayList、HashMap的底層實現,spring、mybatis的相關源碼。問源碼的面試通常就是回去等消息,而後就沒而後了。那時候開始意識到,源碼這東西在以前的工做的中感覺不到,可是在面試中好像面的還挺頻繁的,今後有意識的開始了jdk部分源碼的閱讀(主要是集合)。一開始看源碼,看的特別糙,知道個大概,知道ArrayList的底層實現是數組,HashMap的底層是散列表(數組+鏈表);更深刻一點的擴容、hash碰撞等等就不知道了。數據庫

  讀spring源碼起於工做中遇到了一個問題(spring jdbcTemplate事務,各類詭異,包你醍醐灌頂!),排查一段時間最終是解決了,但過程讓我很是難受,各類上網查資料、各類嘗試,感受就像大海撈針同樣,遙遙無期。我下定決心,我要看一看spring的源碼,因而我買了一本《spring源碼深度解析》,結合着這本書、打開着eclipse,開始了spring的源碼閱讀之旅。至此,讀源碼成了習慣,源碼已經進入了個人內心。設計模式

  後來,springboot的火熱,讓我也想蹭上一蹭,因而有了springboot的啓動源碼系列,雖然還在進行中,可是我相信我能將其完成;工做中用到了shiro,我又結合着《跟我學shiro》將shiro的源碼看了個大概,有了shiro源碼系列博文,還差一篇認證與受權(應該很快就能面世),shiro源碼系列就封筆了。最近在搭建本身的後臺管理系統,用到了quartz,集成的過程也遇到了一些問題,所以有了quartz的三篇文章數組

  慢慢的,從一味的網上找資料變成了不少時候會從源碼中找答案。不求能讀太多的源碼,希望本身接觸的技術都能讀上一讀,路漫漫其修遠兮,吾將上下而求索!

我爲何讀源碼

  不少人必定和我同樣的感覺:源碼在工做中有用嗎? 用處大嗎?很長一段時間內我也有這樣的疑問,認爲哪些有事沒事扯源碼的人就是在裝,只是爲了提升他們的逼格而已。

  那爲何我還要讀源碼呢? 一剛開始爲了面試,後來爲了解決工做中的問題,再後來就是我的喜愛了。說的好聽點是有匠人精神;說的委婉點是好奇(底層是怎麼實現的);說的不自信點是對黑盒的東西我用的沒底,怕用錯;說的簡單直白點是提高自我價值,爲了更高的薪資待遇(這裏對真正的技術迷說聲抱歉)。

  源碼中咱們能夠學到不少東西,學習別人高效的代碼書寫、學習別人對設計模式的熟練使用、學習別人對整個架構的佈局,等等。若是你還能找出其中的不足,那麼恭喜你,你要飛昇了!會使用當然重要,但知道爲何這麼使用一樣重要。從模仿中學習,從模仿中創新。

  讀源碼不像圍城(外面的人想進來,裏面的人想出去),它是外面的人不想進來,裏面的人不想出去;當咱們跨進城內,你會發現(仍是城外好,皮!)城內風光無限,源碼的海洋任咱們遨遊!

                 

  你想好入城了嗎?

我是怎麼樣讀源碼的

  內容瞭解

    首先咱們要對咱們的目標有所瞭解,知道她有什麼特色,有些什麼功能。對對方都還不瞭解,就想着進入別人的心裏世界,那不是臭不要臉嘛,咱們要作一個有着流氓心的紳士;對她有個大體的瞭解了,就能夠發起攻勢,一舉拿下。

    那麼怎麼樣瞭解了,方式有不少,我這裏提供幾種,僅供參考

      最好的方式就是官方參考指南,親生父母每每對孩子是最瞭解的,對孩子的描述也是最詳細的;好比Spring Boot Reference Guide就是對springboot最詳細的描述,怎麼樣使用springboot、springboot特性等等,經過此指南,springboot在你面前一覽無遺;可是,springboot畢竟是外國人的孩子,若是英語很差,估計讀起來有點頭疼了,不過咱們有google翻譯呀,咬咬牙也是能看的。源碼世界的丈母孃、老岳丈是很是慷慨的!

      其次是書籍,國外優秀的有不少,國內也不乏好書,比較推薦此方式,自成體系,讓咱們掌握的知識點不至於太散。這就是比如是源碼的閨蜜,對源碼很是瞭解,重點是挺大方,會盡全力幫助咱們瞭解源碼。

      再次就是博客,雖然可能以爲知識點比較散,可是針對某個知識點卻特別的細,對完全掌握很是有幫助,園子內就有不少技術大牛,寫的博客天然也是很是棒,很是具備學習價值。固然還有社區、論壇、github、碼雲等等。這就是源碼的朋友圈,咱們從中也能獲取到很是多關於源碼的信息。

  設計模式的瞭解

    優秀的框架、技術從不乏設計模式;jdk源碼中就應用了不少設計模式,好比IO流中的適配器模式與裝飾模式、GUI的觀察者模式、集合中的迭代器模式等等;spring源碼中也是用到了大量的設計模式。設計模式有什麼優勢、各適用於什麼場景,不是本文的內容,須要咱們你們自行去了解。

    咱們只須要對一些經常使用的設計模式有個大體瞭解,再去讀源碼是比較好的;不須要將23種設計模式都通讀,也不須要將經常使用設計模式徹底理解透;對於所有通讀,咱們時間有限,另外有些模式確實不太好理解、用的少,性價比不高,不必所有都讀。

    推薦書籍:《Head First Design Patterns》(中文版:《Head First 設計模式》)、《Java與模式》;

    經常使用設計模式:單例模式、工廠模式、適配器模式、裝飾模式、外觀模式、代理模式、迭代器模式、觀察者模式、命令模式

    另外我比較推薦的一種學習設計模式的方式是讀別人博客:java_my_life劉偉技術博客chenssy的設計模式

    設計模式之於源碼,就比如逛街購物之於女人,想順利勾搭源碼,咱們須要好好掌握設計模式這個套路。

  配合ide進行斷點追蹤

    咱們經過源碼的圈子對源碼的瞭解終究只是停在表面,終究仍是沒有走進她的心裏,接下來我就和你們分享下,我是如何走進她的心裏的!

    相信看過個人源碼博客的小夥伴都知道,我很是喜歡經過idea斷點來進行源碼追蹤,斷點追蹤源碼是我很是推薦的一種方式。斷點不只能夠用來調試咱們的代碼,也能夠用來調試咱們用到的框架源碼。面對未知的、茫茫多的源碼,咱們每每沒有足夠的時間、經歷和耐心去通讀全部源碼,咱們只須要去讀咱們關注的部分便可(有人可能會說我都不關心,這...)。那爲何要用斷掉調試的方式來跟源碼,而不是直接從源代碼入手去跟咱們關注的部分呢?嘗試過的小夥伴應該知道,若是咱們對源碼不熟悉,直接經過源碼的方式去跟,一方面很容易迷路(多態,會有不少子類實現),不知道接下來跟哪個,另外一方面也很容易跟丟,當咱們跟入的很深的時候,頗有可能就忘記上一步跟到哪了。

    下面我會舉例來講明我是如何進行斷點追蹤的,以spring-boot-2.0.3之quartz集成,不是你想的那樣哦!spring-boot-2.0.3之quartz集成,數據源問題,源碼探究 爲背景來說,須要搞清楚兩個點:springboot是如何向quartz注入數據源的,quartz是如何操做數據庫的

    springboot向quartz注入數據源

      QuartzAutoConfiguration是springboot自動配置quartz的入口

      將quartz的配置屬性設置給SchedulerFactoryBean;將數據源設置給SchedulerFactoryBean:若是有@QuartzDataSource修飾的數據源,則將@QuartzDataSource修飾的數據源設置給SchedulerFactoryBean,不然將應用的數據源(druid數據源)設置給SchedulerFactoryBean,顯然咱們的應用中沒有@QuartzDataSource修飾的數據源,那麼SchedulerFactoryBean中的數據源就是應用的數據源;將事務管理器設置給SchedulerFactoryBean。SchedulerFactoryBean,負責建立和配置quartz Scheduler,並將其註冊到spring容器中。SchedulerFactoryBean實現InitializingBean的afterPropertiesSet方法,裏面有能夠設置數據源的過程

      能夠看到經過org.quartz.jobStore.dataSource設置的dsName(值爲quartzDs)最後會被替換成springTxDataSource.加scheduler實例名(咱們的應用中是:springTxDataSource.quartzScheduler)。springboot會註冊兩個ConnectionProvider給quartz:一個dsName叫springTxDataSource.quartzScheduler,有事務;一個dsName叫springNonTxDataSource.quartzScheduler,沒事務。

    quartz如何操做數據庫

      咱們經過中止定時任務來跟下quartz對數據庫的操做

      發現quartz用以下方式獲取connection

conn = DBConnectionManager.getInstance().getConnection(getDataSource());

      那麼咱們的job中就能夠按以下方式操做數據庫了

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";                                  // 用此會找不到
    // private String dataSourceName = "springNonTxDataSource.quartzScheduler";     // 不支持事務
    // private String dataSourceName = "springTxDataSource.quartzScheduler";        // 支持事務
    private final String insertSql = "INSERT INTO tbl_sys_user(name, age) VALUES(?,?) ";

    private String schedulerInstanceName = "quartzScheduler";                       // 可經過jobDataMap注入進來

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        String dsName = LocalDataSourceJobStore.NON_TX_DATA_SOURCE_PREFIX
                + schedulerInstanceName;    // 不支持事務
        //String dsName = LocalDataSourceJobStore.TX_DATA_SOURCE_PREFIX + schedulerInstanceName;    // 支持事務
        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;
    }
}
View Code

      明確咱們的目的,找到合適的切入點,進入斷點調試追蹤也就容易了。

  任我說的天花亂墜,你仍無動於衷,那也只是我一廂情願,只有局中人才能體會到其中的奧妙!

總結與感悟

  從上至下所有通讀的方式,我的不太推薦,這是創建在很熟悉的基礎上的,當咱們對某個框架已經比較熟悉了,再從上至下進行通讀,完全瞭解,這是我認爲正確的方式;可是從不熟悉到熟悉這個過程,我的不推薦所有通讀,而是推薦上面我推薦的方式 - 斷點局部追蹤。

  不少時候,咱們的博文都只是授之以魚,而咱們也只是從中獲得魚;而這篇的目的則是授之以漁,我但願你們從中學到捕魚的方法,而不是一味的等待別人的魚;但願你們可以自給自足,也能把魚和漁都授予其餘人。

  只要咱們開始去讀源碼,慢慢的就會造成本身的一套讀源碼的方式;每一個人的方式都不同,合適本身的纔是最好的。行動起來,用合適的方式去俘獲你的的她吧!

  純屬我的之拙見,不喜請噴!

相關文章
相關標籤/搜索