from:http://mingxinglai.com/cn/2015/12/material-of-mysql/

          我這里推薦幾本MySQL的好書(shū),應(yīng)該能夠有效避免學(xué)習(xí)MySQL的彎路,并且達(dá)到一個(gè)不錯(cuò)的水平。 我這里推薦的書(shū)或材料分為兩個(gè)部分,分別是MySQL的使用和MySQL的源碼學(xué)習(xí)。在介紹的過(guò)程中,我會(huì)穿插簡(jiǎn)單的評(píng)語(yǔ)或感想。

          1.MySQL的使用

          1.1 MySQL技術(shù)內(nèi)幕:InnoDB存儲(chǔ)引擎

          學(xué)習(xí)MySQL的使用,首推姜承堯的《MySQL技術(shù)內(nèi)幕:InnoDB存儲(chǔ)引擎》,當(dāng)然不是因?yàn)榻猻ir是我的經(jīng)理才推薦這本書(shū)。這本書(shū)確實(shí)做到了由漸入深、深入淺出,是中國(guó)人寫(xiě)的最贊的MySQL技術(shù)書(shū)籍,符合國(guó)人的思維方式和閱讀習(xí)慣,而且,這本書(shū)簡(jiǎn)直就是面試寶典,對(duì)于近期有求職MySQL相關(guān)崗位的朋友,可以認(rèn)真閱讀,對(duì)找工作有很大的幫助。當(dāng)然,也有人說(shuō)這本書(shū)入門(mén)難度較大,這個(gè)就自己取舍了,個(gè)人建議就以這本書(shū)入門(mén)即可,有不懂的地方可以求助官方手冊(cè)和google。

          MySQL技術(shù)內(nèi)幕

          1.2 MySQL的官方手冊(cè)

          我剛開(kāi)始學(xué)習(xí)MySQL的時(shí)候誤區(qū)就是,沒(méi)有好好閱讀MySQL的官方手冊(cè)。例如,我剛開(kāi)始很難理解InnoDB的鎖,尤其是各個(gè)情況下如何加鎖,這個(gè)問(wèn)題在我?guī)煹苓M(jìn)入百度做DBA時(shí),也困擾了他一陣子,我們兩還討論來(lái)討論去,其實(shí),MySQL官方手冊(cè)已經(jīng)寫(xiě)得清清楚楚,什么樣的SQL語(yǔ)句加什么樣的鎖,當(dāng)然,MySQL的官方手冊(cè)非常龐大,一時(shí)半會(huì)很難看完,建議先看InnoDB相關(guān)的部分。

          http://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html

          1.3 MySQL排錯(cuò)指南

          MySQL排錯(cuò)指南》是2015年夏天引入中國(guó)的書(shū)籍,這本書(shū)可以說(shuō)是DBA速成指南,介紹的內(nèi)容其實(shí)比較簡(jiǎn)單,但是也非常實(shí)用,對(duì)于DBA這個(gè)講究經(jīng)驗(yàn)的工種,這本書(shū)就是傳授經(jīng)驗(yàn)的,可能對(duì)有較多工作經(jīng)驗(yàn)的DBA來(lái)說(shuō),這本書(shū)基本沒(méi)有什么用,但是,對(duì)于剛?cè)肼殘?chǎng)的新人,或?qū)W校里的學(xué)生,這本書(shū)會(huì)有較大的幫助,非常推薦。

          MySQL排錯(cuò)指南

          1.4 高性能MySQL

          高性能MySQL》是MySQL領(lǐng)域的經(jīng)典之作,擁有廣泛的影響力,學(xué)習(xí)MySQL的朋友都應(yīng)該有所耳聞,所以我就不作過(guò)多介紹,唯一的建議就是仔細(xì)看、認(rèn)真看、多看幾遍,我每次看都會(huì)有不小的收獲。這就是一本雖然書(shū)很厚,但是需要一頁(yè)一頁(yè)、一行一行都認(rèn)真看的書(shū)。

          高性能MySQL

          1.5 數(shù)據(jù)庫(kù)索引設(shè)計(jì)與優(yōu)化

          如果認(rèn)真學(xué)習(xí)完前面幾本書(shū),基本上都已經(jīng)對(duì)MySQL掌握得不錯(cuò)了,但是,如果不了解如何設(shè)計(jì)一個(gè)好的索引,仍然不能成為牛逼的DBA,牛逼的DBA和不牛逼的DBA,一半就是看對(duì)索引的掌握情況,《數(shù)據(jù)庫(kù)索引設(shè)計(jì)與優(yōu)化》就是從普通DBA走向牛逼DBA的捷徑,這本書(shū)在淘寶內(nèi)部非常推崇,但是在中國(guó)名氣卻不是很大,很多人不了解。這本書(shū)也是今年夏天剛有中文版本的,非常值得入手以后跟著練習(xí),雖然知道的人不多,豆瓣上也幾乎沒(méi)有什么評(píng)價(jià),但是,強(qiáng)烈推薦、吐血推薦!

          數(shù)據(jù)庫(kù)索引設(shè)計(jì)與優(yōu)化

          1.6 Effective MySQL系列

          Effective MySQL系列》是指:

          • Effective MySQL Replication Techniques in Depth
          • Effective MySQL之SQL語(yǔ)句最優(yōu)化
          • Effective MySQL之備份與恢復(fù)

          effective

          這一系列并不如前面推薦的好,其中,我只看了前兩本,這幾本書(shū)只能算是小冊(cè)子,如果有時(shí)間可以看看,對(duì)某一個(gè)”模塊”進(jìn)入深入了解。

          2.MySQL的源碼

          關(guān)于MySQL源碼的書(shū)非常少,還好現(xiàn)在市面上有兩本不錯(cuò)的書(shū),而且剛好一本講server層,一本講innodb存儲(chǔ)引擎層,對(duì)于學(xué)習(xí)MySQL源碼會(huì)很有幫助,至少能夠更加快速地了解MySQL的原理和宏觀結(jié)構(gòu),然后再深入細(xì)節(jié)。此外,還有一些博客或PPT將得也很不錯(cuò),這里推薦最好的幾份材料。

          2.1 InnoDB - A journey to the core

          InnoDB - A journey to the core》 是MySQL大牛Jeremy Cole寫(xiě)的PPT,介紹InnoDB的存儲(chǔ)模塊,即表空間、區(qū)、段、頁(yè)的格式、記錄的格式、槽等等。是學(xué)習(xí)Innodb存儲(chǔ)的最好的材料。感謝Jeremy Cole!

          2.2 深入MySQL源碼

          登博的分享《深入MySQL源碼》,相信很多想了解MySQL源碼的朋友已經(jīng)知道這份PPT,就不過(guò)多介紹,不過(guò),要多說(shuō)一句,登博的參考資料里列出的幾個(gè)博客,都要關(guān)注一下,干貨滿滿,是學(xué)習(xí)MySQL必須關(guān)注的博客。

          2.3 深入理解MySQL核心技術(shù)

          深入理解MySQL核心技術(shù)》是第一本關(guān)于MySQL源碼的書(shū),著重介紹了MySQL的Server層,重點(diǎn)介紹了宏觀架構(gòu),對(duì)于剛開(kāi)始學(xué)習(xí)MySQL源碼的人,相信會(huì)有很大的幫助,我在學(xué)習(xí)MySQL源碼的過(guò)程中,反復(fù)的翻閱了幾遍,這本書(shū)剛開(kāi)始看的時(shí)候會(huì)很痛苦,但是,對(duì)于研究MySQL源碼,非常有幫助,就看你是否需要,如果沒(méi)有研究MySQL源碼的決心,這本書(shū)應(yīng)該會(huì)被唾棄。

          深入理解MySQL核心技術(shù)

          2.4 MySQL內(nèi)核:InnoDB存儲(chǔ)引擎

          我們組的同事寫(xiě)的《MySQL內(nèi)核:InnoDB存儲(chǔ)引擎》,可能宇宙范圍內(nèi)這本書(shū)就數(shù)我學(xué)得最認(rèn)真了,雖然書(shū)中有很多編輯錯(cuò)誤,但是,平心而論,還是寫(xiě)得非常好的,相對(duì)于《深入理解MySQL核心技術(shù)》,可讀性更強(qiáng)一些,建議研究Innodb存儲(chǔ)引擎的朋友,可以了解一下,先對(duì)Innodb有一個(gè)宏觀的概念,對(duì)大致原理有一個(gè)整體的了解,然后再深入細(xì)節(jié),肯定會(huì)比自己從頭開(kāi)始研究會(huì)快很多,這本書(shū)可以幫助你事半功倍。

          MySQL內(nèi)核

          2.5 MySQL Internals Manual

          MySQL Internals Manual》相對(duì)于MySQL Manual來(lái)說(shuō),寫(xiě)的太粗糙,誰(shuí)讓人家是官方文檔呢,研究MySQL源碼的時(shí)候可以簡(jiǎn)單地參考一下,但是,還是不要指望文檔能夠回答你的問(wèn)題,還需要看代碼才行。

          http://dev.mysql.com/doc/internals/en/

          2.6 MariaDB原理與實(shí)現(xiàn)

          評(píng)論里提到的《MariaDB原理與實(shí)現(xiàn)》我也買(mǎi)了一本,還不錯(cuò),MariaDB講的并不多,重點(diǎn)講了Group Commit、線程池和復(fù)制的實(shí)現(xiàn),都是MySQL Server層的知識(shí),對(duì)MySQL Server層感興趣的可以參考一下。

          MariaDB

          3. 后記

          希望這里推薦的材料對(duì)學(xué)習(xí)MySQL的同學(xué)、朋友有所幫助,也歡迎推薦靠譜的學(xué)習(xí)材料,大家共同進(jìn)步。

          posted @ 2018-12-03 15:54 小馬歌 閱讀(327) | 評(píng)論 (0)編輯 收藏
           
               摘要: from:https://yq.aliyun.com/articles/69520?utm_content=m_10360#6摘要: # 我的問(wèn)題排查工具箱 ## 前言 平時(shí)的工作中經(jīng)常碰到很多疑難問(wèn)題的處理,在解決問(wèn)題的同時(shí),有一些工具起到了相當(dāng)大的作用,在此書(shū)寫(xiě)下來(lái),一是作為筆記,可以讓自己后續(xù)忘記了可快速翻閱,二是分享,希望看到此文的同學(xué)們可以拿出自己日常覺(jué)得幫助很大的工具,大家一...  閱讀全文
          posted @ 2018-11-23 10:47 小馬歌 閱讀(281) | 評(píng)論 (0)編輯 收藏
           
               摘要: from:https://www.cnblogs.com/Anker/p/3271773.html1、前言  之前在看《unix環(huán)境高級(jí)編程》第八章進(jìn)程時(shí)候,提到孤兒進(jìn)程和僵尸進(jìn)程,一直對(duì)這兩個(gè)概念比較模糊。今天被人問(wèn)到什么是孤兒進(jìn)程和僵尸進(jìn)程,會(huì)帶來(lái)什么問(wèn)題,怎么解決,我只停留在概念上面,沒(méi)有深入,倍感慚愧。晚上回來(lái)google了一下,再次參考APUE,認(rèn)真總結(jié)一下,加深理解。2、基本概念  我...  閱讀全文
          posted @ 2018-09-03 19:53 小馬歌 閱讀(223) | 評(píng)論 (0)編輯 收藏
           
          from:http://blog.csdn.net/vfush/article/details/51086274

          最近做了個(gè)Web項(xiàng)目, 架構(gòu)上使用了 Nginx +tomcat 集群, 且全站HTTPS,用nginx 做負(fù)載,nginx和tomcat 使用內(nèi)網(wǎng)http通信,遇到http css,js靜態(tài)資源被瀏覽器攔截問(wèn)題,網(wǎng)上搜索到的很多文章在描述 Nginx + Tomcat 啟用 HTTPS 支持的時(shí)候,都必須在 Nginx 和 Tomcat 兩邊同時(shí)配置 SSL 支持,今天做個(gè)總結(jié)。

          遇到問(wèn)題

          1. nginx強(qiáng)制使用https訪問(wèn)(http跳轉(zhuǎn)到https)
          2. http的js,css 等靜態(tài)資源被瀏覽器攔截(http不被信任)

          最后的解決方案

          首先解決第一個(gè)問(wèn)題全站https 
          參考 
          三種方式,跟大家共享一下

          nginx的rewrite方法

          server {   listen  192.168.1.111:80;   server_name test.com;   rewrite ^(.*)$  https://$host$1 permanent; }   

          nginx的497狀態(tài)碼,我選擇了這種方式

          server {       listen       192.168.1.11:443;  #ssl端口       listen       192.168.1.11:80;   #用戶(hù)習(xí)慣用http訪問(wèn),加上80,后面通過(guò)497狀態(tài)碼讓它自動(dòng)跳到443端口       server_name  test.com;       #為一個(gè)server{......}開(kāi)啟ssl支持       ssl                  on;       #指定PEM格式的證書(shū)文件        ssl_certificate      /etc/nginx/test.pem;        #指定PEM格式的私鑰文件       ssl_certificate_key  /etc/nginx/test.key;        #讓http請(qǐng)求重定向到https請(qǐng)求        error_page 497  https://$host$uri?$args;   }   

          index.html刷新網(wǎng)頁(yè)

          <html>   <meta http-equiv="refresh" content="0;url=https://test.com/">   </html>  

          當(dāng)http訪問(wèn)到index.html時(shí)候自動(dòng)跳轉(zhuǎn)到https


          接下來(lái)解決第二個(gè)問(wèn)題 
          如果tomcat 和nginx 雙方?jīng)]有配置X-Forwarded-Proto tomcat就不能正確區(qū)分實(shí)際用戶(hù)是http 還是https,導(dǎo)致tomcat 里配置的靜態(tài)資源被認(rèn)為是http而被瀏覽器攔截,request.getScheme()總是 http,而不是實(shí)際的http或https

          分別配置一下 Nginx 和 Tomcat ,果然好了。 
          配置 Nginx 的轉(zhuǎn)發(fā)選項(xiàng):

           proxy_set_header       Host $host;       proxy_set_header  X-Real-IP  $remote_addr;       proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;       proxy_set_header X-Forwarded-Proto  $scheme;  
          • 1
          • 2
          • 3
          • 4

          配置Tomcat server.xml 的 Engine 模塊下配置一個(gè) Valve:

          <Valve className="org.apache.catalina.valves.RemoteIpValve"   remoteIpHeader="X-Forwarded-For"   protocolHeader="X-Forwarded-Proto"   protocolHeaderHttpsValue="https"/>  
          • 1
          • 2
          • 3
          • 4

          非80端口配置 
          Nginx增加以下配置 
          proxy_set_header Host $host:$server_port; 非80端口 ,用80端口時(shí) 不需要$server_port 
          proxy_set_header X-Real-IP $remote_addr; 
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
          proxy_set_header X-Forwarded-Proto $scheme; 

          Tomcat server.xml配置 
          <Engine name="Catalina" defaultHost="localhost"> 
          <Valve className="org.apache.catalina.valves.RemoteIpValve" 
          remoteIpHeader="X-Forwarded-For" 
          protocolHeader="X-Forwarded-Proto" 
          protocolHeaderHttpsValue="https" httpsServerPort="7001"/> 非80端口時(shí),必須增加httpsServerPort配置,不然request.getServerPort()方法返回 443. 
          </Engine>

          關(guān)于 RemoteIpValve,可以閱讀下 doc

          http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html

          posted @ 2017-10-12 11:02 小馬歌 閱讀(409) | 評(píng)論 (0)編輯 收藏
           
          gerrit還是輕易不要嘗試引入,它的權(quán)限管理,真是復(fù)雜極了。對(duì)于小型團(tuán)隊(duì),初期這將是個(gè)噩夢(mèng),但是對(duì)于像OpenStack,安卓這種大型team,又是一把利器。
          下面嘗試測(cè)試了兩個(gè)用戶(hù)的簡(jiǎn)單情況,很多配置都是系統(tǒng)默認(rèn),沒(méi)有進(jìn)行啥復(fù)雜配置,即使這樣也是錯(cuò)誤百出,光一個(gè)commit就要折騰半天,而且還有些機(jī)制沒(méi)搞清楚。
          首先要做的準(zhǔn)備工作就是準(zhǔn)備兩個(gè)gerrit用戶(hù),user1和user2,并且分別把user1和user2的ssh pub-key通過(guò)gerrit setting添加好。
          1. 首先user1創(chuàng)建一個(gè)叫HelloWord的project。
             如何創(chuàng)建project請(qǐng)參考前期博客或者官方文檔。
          2. user1在自己的工作環(huán)境中把HelloWord clone下來(lái)
          [user1@jenkins ~]$ git clone ssh://user1@gerrit.example.com:29418/HelloWorld.git
          Initialized empty Git repository in /home/user1/HelloWorld/.git/
          remote: Counting objects: 2, done
          remote: Finding sources: 100% (2/2)
          remote: Total 2 (delta 0), reused 0 (delta 0)
          Receiving objects: 100% (2/2), done.
          加入user1沒(méi)有添加ssh pubkey的話,這一步會(huì)出permission deny

          clone后,創(chuàng)建一個(gè)README文件并add,commit
          [user1@jenkins ~]$ cd HelloWorld
          [user1@jenkins HelloWorld]$ ls
          [user1@jenkins HelloWorld]$ touch README
          [penxiao@jenkins test]$ git add README 
          [penxiao@jenkins test]$ git commit -m add README
          這里注意一點(diǎn),在下面要push之前,一定要配置好git config的 username和email
          可以通過(guò)命令行或者直接編輯 ~/.gitconfig文件實(shí)現(xiàn),而且email一定要和gerrit里注冊(cè)的email一致,否者push也會(huì)出錯(cuò)。
          [user1@jenkins HelloWorld]$ git push origin master
          Counting objects: 3, done.
          Writing objects: 100% (3/3), 213 bytes, done.
          Total 3 (delta 0), reused 0 (delta 0)
          remote: Processing changes: refs: 1, done    
          To ssh://user1@gerrit.example.com:29418/HelloWorld.git
           * [new branch]      master -> master
          [user1@jenkins HelloWorld]$

          在gerrit的gitweb鏈接可以查看push的文件。
          3. user2加入
          [user2@jenkins ~]$ git clone ssh://user1@gerrit.example.com:29418/HelloWorld.git
          Initialized empty Git repository in /home/user2/HelloWorld/.git/
          remote: Counting objects: 3, done
          remote: Finding sources: 100% (3/3)
          remote: Total 3 (delta 0), reused 3 (delta 0)
          Receiving objects: 100% (3/3), done.
          [user2@jenkins ~]$ cd HelloWorld
          [user2@jenkins HelloWorld]$ ls
          README
          [user2@jenkins HelloWorld]$ 
          user2對(duì)README文件進(jìn)行修改,然后要commit,push
          !!!也同樣注意,user2的git config,username和email的配置,email要和gerrit setting里的一致。
          commit完以后可以看到
          [user2@jenkins HelloWorld]$ git log
          commit 7959fe47bc2d2f53539a1861aa6b0d71afe0a531
          Author: user2 <user2@gerrit.com>
          Date:   Thu Dec 12 00:24:53 2013 -0500
              edit README
          commit 98099fc0de3ba889b18cf36f9a5af267b3ddb501
          Author: user1 <user@gerrit.com>
          Date:   Thu Dec 12 00:15:08 2013 -0500
              add README
          [user2@jenkins HelloWorld]$
          現(xiàn)在user2要把這次的改變push到gerrit,可以么?
          不行的,可以看到
          [user2@jenkins HelloWorld]$ git push origin master
          Counting objects: 5, done.
          Writing objects: 100% (3/3), 249 bytes, done.
          Total 3 (delta 0), reused 0 (delta 0)
          remote: Branch refs/heads/master:
          remote: You are not allowed to perform this operation.
          remote: To push into this reference you need 'Push' rights.
          remote: User: user2
          remote: Please read the documentation and contact an administrator
          remote: if you feel the configuration is incorrect
          remote: Processing changes: refs: 1, done    
          To ssh://user2@gerrit.example.com:29418/HelloWorld.git
           ! [remote rejected] master -> master (prohibited by Gerrit)
          error: failed to push some refs to 'ssh://user2@gerrit.example.com:29418/HelloWorld.git'
          [user2@jenkins HelloWorld]$ 
          這就是gerrit的精髓所在了。原因是gerrit不允許直接將本地修改同步到遠(yuǎn)程倉(cāng)庫(kù)。客戶(hù)機(jī)必須先push到遠(yuǎn)程倉(cāng)庫(kù)的refs/for/*分支上,等待審核。這也是為什么我們需要使用gerrit的原因。gerrit本身就是個(gè)代碼審核工具。

          接下來(lái)更該push的地址:  
          [user2@jenkins HelloWorld]$git config remote.origin.push refs/heads/*:refs/for/*  
          此命令實(shí)際是更改的是本地倉(cāng)庫(kù)test_project/.git/config文件。 
          再次push   
          [user2@jenkins HelloWorld]$git push origin  
          這次不要加master
          [user2@jenkins HelloWorld]$ git push origin
          Counting objects: 5, done.
          Writing objects: 100% (3/3), 249 bytes, done.
          Total 3 (delta 0), reused 0 (delta 0)
          remote: Processing changes: refs: 1, done    
          remote: ERROR: missing Change-Id in commit message footer
          remote: Suggestion for commit message:
          remote: edit README
          remote: 
          remote: Change-Id: I7959fe47bc2d2f53539a1861aa6b0d71afe0a531
          remote: 
          remote: Hint: To automatically insert Change-Id, install the hook:
          remote:   gitdir=$(git rev-parse --git-dir); scp -p -P 29418 user2@gerrit.example.com:hooks/commit-msg ${gitdir}/hooks/
          remote: 
          remote: 
          To ssh://user2@gerrit.example.com:29418/HelloWorld.git
           ! [remote rejected] master -> refs/for/master (missing Change-Id in commit message footer)
          error: failed to push some refs to 'ssh://user2@gerrit.example.com:29418/HelloWorld.git'
          尼瑪,還是不行,說(shuō)缺change-Id,為了能讓每次commit能自己insert 這個(gè)change-id,需要從gerrit server上下載一個(gè)腳本
          [user2@jenkins HelloWorld] scp -p 29418 user2@gerrit.example.com:hooks/commit-msg <local path to your git>/.git/hooks/
          然后重新commit
          [user2@jenkins HelloWorld]$ git commit --amend
          再次查看git log
          [user2@jenkins HelloWorld]$ git log
          commit f6b5919170875b5b4870fca2ab906c516c97006e
          Author: user2 <user2@gerrit.com>
          Date:   Thu Dec 12 00:24:53 2013 -0500
              edit by user2
              
              Change-Id: Ieac68bebefee7c6d4237fa5c058386bf7c4f66b7
          commit 98099fc0de3ba889b18cf36f9a5af267b3ddb501
          Author: user1 <user1@gerrit.com>
          Date:   Thu Dec 12 00:15:08 2013 -0500
              add README
          [user2@jenkins HelloWorld]$ 
          這次就有了change id
          然后再次push
          [user2@jenkins HelloWorld]$ git push origin
          Counting objects: 5, done.
          Writing objects: 100% (3/3), 289 bytes, done.
          Total 3 (delta 0), reused 0 (delta 0)
          remote: Processing changes: new: 1, refs: 1, done    
          remote: 
          remote: New Changes:
          remote:   http://gerrit.example.com:8080/1
          remote: 
          To ssh://user2@gerrit.example.com:29418/HelloWorld.git
           * [new branch]      master -> refs/for/master
          [user2@jenkins HelloWorld]$ 
          posted @ 2017-08-11 11:23 小馬歌 閱讀(259) | 評(píng)論 (0)編輯 收藏
           

          from:http://blog.csdn.net/acmman/article/details/50848595

          版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。

          目前,業(yè)內(nèi)關(guān)于OSGI技術(shù)的學(xué)習(xí)資源或者技術(shù)文檔還是很少的。我在某寶網(wǎng)搜索了一下“OSGI”的書(shū)籍,結(jié)果倒是有,但是種類(lèi)少的可憐,而且?guī)缀鯖](méi)有人購(gòu)買(mǎi)。
          因?yàn)楣ぷ鞯脑蛭倚枰獙W(xué)習(xí)OSGI,所以我不得不想盡辦法來(lái)主動(dòng)學(xué)習(xí)OSGI。我將用文字記錄學(xué)習(xí)OSGI的整個(gè)過(guò)程,通過(guò)整理書(shū)籍和視頻教程,來(lái)讓我更加了解這門(mén)技術(shù),同時(shí)也讓需要學(xué)習(xí)這門(mén)技術(shù)的同志們有一個(gè)清晰的學(xué)習(xí)路線。

          我們需要解決一下幾問(wèn)題:
          1.如何正確的理解和認(rèn)識(shí)OSGI技術(shù)?

          我們從外文資料上或者從翻譯過(guò)來(lái)的資料上看到OSGi解釋和定義,都是直譯過(guò)來(lái)的,但是OSGI的真實(shí)意義未必是中文直譯過(guò)來(lái)的意思。OSGI的解釋就是Open Service Gateway Initiative,直譯過(guò)來(lái)就是“開(kāi)放的服務(wù)入口(網(wǎng)關(guān))的初始化”,聽(tīng)起來(lái)非常費(fèi)解,什么是服務(wù)入口初始化?

          所以我們不去直譯這個(gè)OSGI,我們換一種說(shuō)法來(lái)描述OSGI技術(shù)。

          我們來(lái)回到我們以前的某些開(kāi)發(fā)場(chǎng)景中去,假設(shè)我們使用SSH(struts+spring+hibernate)框架來(lái)開(kāi)發(fā)我們的Web項(xiàng)目,我們做產(chǎn)品設(shè)計(jì)和開(kāi)發(fā)的時(shí)候都是分模塊的,我們分模塊的目的就是實(shí)現(xiàn)模塊之間的“解耦”,更進(jìn)一步的目的是方便對(duì)一個(gè)項(xiàng)目的控制和管理。
          我們對(duì)一個(gè)項(xiàng)目進(jìn)行模塊化分解之后,我們就可以把不同模塊交給不同的開(kāi)發(fā)人員來(lái)完成開(kāi)發(fā),然后項(xiàng)目經(jīng)理把大家完成的模塊集中在一起,然后拼裝成一個(gè)最終的產(chǎn)品。一般我們開(kāi)發(fā)都是這樣的基本情況。

          那么我們開(kāi)發(fā)的時(shí)候預(yù)計(jì)的是系統(tǒng)的功能,根據(jù)系統(tǒng)的功能來(lái)進(jìn)行模塊的劃分,也就是說(shuō),這個(gè)產(chǎn)品的功能或客戶(hù)的需求是劃分的重要依據(jù)。

          但是我們?cè)陂_(kāi)發(fā)過(guò)程中,我們模塊之間還要彼此保持聯(lián)系,比如A模塊要從B模塊拿到一些數(shù)據(jù),而B(niǎo)模塊可能要調(diào)用C模塊中的一些方法(除了公共底層的工具類(lèi)之外)。所以這些模塊只是一種邏輯意義上的劃分。

          最重要的一點(diǎn)是,我們把最終的項(xiàng)目要去部署到tomcat或者jBoss的服務(wù)器中去部署。那么我們啟動(dòng)服務(wù)器的時(shí)候,能不能關(guān)閉項(xiàng)目的某個(gè)模塊或功能呢?很明顯是做不到的,一旦服務(wù)器啟動(dòng),所有模塊就要一起啟動(dòng),都要占用服務(wù)器資源,所以關(guān)閉不了模塊,假設(shè)能強(qiáng)制拿掉,就會(huì)影響其它的功能。

          以上就是我們傳統(tǒng)模塊式開(kāi)發(fā)的一些局限性。

          我們做軟件開(kāi)發(fā)一直在追求一個(gè)境界,就是模塊之間的真正“解耦”、“分離”,這樣我們?cè)谲浖墓芾砗烷_(kāi)發(fā)上面就會(huì)更加的靈活,甚至包括給客戶(hù)部署項(xiàng)目的時(shí)候都可以做到更加的靈活可控。但是我們以前使用SSH框架等架構(gòu)模式進(jìn)行產(chǎn)品開(kāi)發(fā)的時(shí)候我們是達(dá)不到這種要求的。

          所以我們“架構(gòu)師”或頂尖的技術(shù)高手都在為模塊化開(kāi)發(fā)努力的摸索和嘗試,然后我們的OSGI的技術(shù)規(guī)范就應(yīng)運(yùn)而生。

          現(xiàn)在我們的OSGI技術(shù)就可以滿足我們之前所說(shuō)的境界:在不同的模塊中做到徹底的分離,而不是邏輯意義上的分離,是物理上的分離,也就是說(shuō)在運(yùn)行部署之后都可以在不停止服務(wù)器的時(shí)候直接把某些模塊拿下來(lái),其他模塊的功能也不受影響。

          由此,OSGI技術(shù)將來(lái)會(huì)變得非常的重要,因?yàn)樗趯?shí)現(xiàn)模塊化解耦的路上,走得比現(xiàn)在大家經(jīng)常所用的SSH框架走的更遠(yuǎn)。這個(gè)技術(shù)在未來(lái)大規(guī)模、高訪問(wèn)、高并發(fā)的Java模塊化開(kāi)發(fā)領(lǐng)域,或者是項(xiàng)目規(guī)范化管理中,會(huì)大大超過(guò)SSH等框架的地位。

          現(xiàn)在主流的一些應(yīng)用服務(wù)器,Oracle的weblogic服務(wù)器,IBM的WebSphere,JBoss,還有Sun公司的glassfish服務(wù)器,都對(duì)OSGI提供了強(qiáng)大的支持,都是在OSGI的技術(shù)基礎(chǔ)上實(shí)現(xiàn)的。有那么多的大型廠商支持OSGI這門(mén)技術(shù),我們既可以看到OSGI技術(shù)的重要性。所以將來(lái)OSGI是將來(lái)非常重要的技術(shù)。

          但是OSGI仍然脫離不了框架的支持,因?yàn)镺SGI本身也使用了很多spring等框架的基本控件(因?yàn)橐獙?shí)現(xiàn)AOP依賴(lài)注入等功能),但是哪個(gè)項(xiàng)目又不去依賴(lài)第三方j(luò)ar呢?



          2.OSGI技術(shù)對(duì)我們項(xiàng)目的開(kāi)發(fā)有什么幫助?

          (1)項(xiàng)目展示
          接下來(lái)我們同過(guò)項(xiàng)目代碼來(lái)展示一下OSGI的魅力:
          我們先不要去急著理解如何使用OSGI,我們通過(guò)一個(gè)項(xiàng)目先來(lái)看一下OSGI的效果。
          (以下工程代碼是網(wǎng)上教學(xué)視頻中的樣例,源碼我這里是沒(méi)有的)
          (提前說(shuō)一下:我們要學(xué)習(xí)的重點(diǎn)就是我們這個(gè)購(gòu)物網(wǎng)站如何結(jié)合OSGI技術(shù),使得項(xiàng)目更加的靈活可控,而購(gòu)物網(wǎng)站本身并不是重點(diǎn)。)


          首先在Eclipse中先打開(kāi)我們的單服務(wù)器版本的項(xiàng)目:

          啟動(dòng)成功:




          這是一個(gè)Web項(xiàng)目,我們打開(kāi)瀏覽器看一下效果:

          可以看出是一個(gè)網(wǎng)上購(gòu)物的項(xiàng)目。

          我們來(lái)看一下我們基于OSGI技術(shù)的項(xiàng)目和我們一般的項(xiàng)目有什么區(qū)別。
          首先介紹一下這個(gè)項(xiàng)目的模塊:

          1.大類(lèi)展示


          2.小類(lèi)展示(大類(lèi)的子產(chǎn)品)

          點(diǎn)進(jìn)去之后就是產(chǎn)品的具體信息



          3.購(gòu)物車(chē)
          沒(méi)買(mǎi)東西是空的:

          買(mǎi)完之后:



          4.商品管理(上架、下架)



          可以看到,這個(gè)項(xiàng)目和我們平常開(kāi)發(fā)的項(xiàng)目沒(méi)有什么不同(我知道界面很簡(jiǎn)陋= =),重點(diǎn)是它的啟動(dòng)和加載過(guò)程。


          (2)關(guān)于服務(wù)器
          我們是通過(guò)動(dòng)態(tài)加載,也就是“熱部署”來(lái)啟動(dòng)我們的項(xiàng)目的。就是說(shuō),我們這個(gè)項(xiàng)目把它放在Web容器中之后,我們可以將某些功能給它拿下來(lái),而且拿下來(lái)的時(shí)候不會(huì)對(duì)其他模塊造成影響。

          我們以前運(yùn)行tomcat的時(shí)候,啟動(dòng)一下服務(wù)器,將Web項(xiàng)目一次性裝載完畢,控制臺(tái)會(huì)出現(xiàn)類(lèi)似這種信息:


          但是我們啟動(dòng)這個(gè)項(xiàng)目的時(shí)候并不是這樣:


          那么我們沒(méi)有用tomcat和jBoss,那是如何部署和啟動(dòng)Web項(xiàng)目的呢?不可能沒(méi)有Web服務(wù)器中間件的啊?這里告訴大家,OSGI技術(shù)里面也是內(nèi)嵌了一個(gè)Web服務(wù)器的,就是jetty。


          我們打開(kāi)這個(gè)項(xiàng)目的Run Configuration配置窗口,看一下運(yùn)行這個(gè)項(xiàng)目所需要的插件包:


          可以看到,除了一些Web項(xiàng)目需要的jar包,還是有jetty的存在的。所以用到的服務(wù)器是jetty,不再是tomcat。


          大家可能還是比較熟悉tomcat,對(duì)于jetty不是太熟悉,那么我們簡(jiǎn)單介紹一下jetty:
          jetty也是一個(gè)比較優(yōu)秀的Web容器,在某些性能方面要比tomcat強(qiáng)大的多(如高并發(fā),長(zhǎng)連接)。而且它的整個(gè)結(jié)構(gòu)比tomcat輕巧很多(tomcat更臃腫),具體區(qū)別大家可以去網(wǎng)上自己看一下。


          (3)運(yùn)行模式和插件
          我們接下來(lái)正式看一下此項(xiàng)目在OSGI下的運(yùn)行模式:
          我們?cè)趩?dòng)的時(shí)候,加載了四個(gè)模塊,分別是:

          按照模塊化的思想他們就是四個(gè)對(duì)應(yīng)的功能模塊。
          他們對(duì)應(yīng)的四個(gè)功能模塊的工程代碼我們可以在Eclipse中看到:



          我們看一下我們的啟動(dòng)配置(依然打開(kāi)是Run Configuration配置窗口):


          配置分為“WorkSpace”和“Target Platform”,分別是我們工作空間(我們自己寫(xiě)的項(xiàng)目模塊和工具類(lèi))的插件和運(yùn)行平臺(tái)(一些依賴(lài)jar的配置)的插件,兩者結(jié)合啟動(dòng)我們的項(xiàng)目就會(huì)正常運(yùn)行。

          我們啟動(dòng)項(xiàng)目之后,在控制臺(tái)輸入指令“ss”,就會(huì)出現(xiàn)我們所有加載的插件的運(yùn)行情況:


          一啟動(dòng)的時(shí)候,它會(huì)首先加載Eclipse的OSGI插件(Eclipse本身也是一種OSGI的容器):

          我們打開(kāi)我們的Eclipse安裝目錄,然后找到plugins文件夾,可以看到Eclipse所有的插件:

          可以看到有文件夾形式的,有jar形式的插件。

          我們?cè)趺慈ダ斫獠寮兀?br />插件其實(shí)就是被開(kāi)發(fā)工具或OSGI容器管理和配置起來(lái)的jar包。  

          我們隨便打開(kāi)一個(gè)文件夾類(lèi)型的插件,可以看到:

          可以看到里面除了lib之外還有其它東西,然后有一個(gè)“OSGI-INF”文件夾。且不管它是什么,這都足以說(shuō)明我們的Eclipse就是一個(gè)OSGI容器。

          (4)熱部署和熱啟動(dòng)
          我們接下來(lái)回到重點(diǎn),在我們啟動(dòng)的過(guò)程中,我們不停止運(yùn)行,然后去停掉其中的一個(gè)模塊:

          假如我們要停掉“管理”模塊:

          也就是停掉id為22的插件

          結(jié)果:


          然后刷新我們的網(wǎng)站主頁(yè)面:

          發(fā)現(xiàn)我們的“管理”模塊消失了!

          這個(gè)模塊的消失并不是javascript的技術(shù),而是一種服務(wù)器技術(shù),我們是通過(guò)服務(wù)器內(nèi)部把它動(dòng)態(tài)卸載掉的。

          我們的管理模塊去掉之后,網(wǎng)站的其它功能不受任何影響。至此我們的服務(wù)器沒(méi)有進(jìn)行任何的暫停或關(guān)閉。

          我們?cè)偻5?#8220;購(gòu)物車(chē)”模塊:

          效果:

          其它模塊依舊不受影響。

          我們關(guān)閉了兩個(gè)模塊,現(xiàn)在輸入ss看一下所有插件和模塊的運(yùn)行情況:

          可以看到我們的兩個(gè)模塊處于RESOLVED狀態(tài),也就是待解決狀態(tài)。

          當(dāng)然我們也可以將我們的模塊在服務(wù)器開(kāi)啟狀態(tài)下部署上去:
          如我們啟動(dòng)購(gòu)物車(chē)模塊:


          發(fā)現(xiàn)購(gòu)物車(chē)回來(lái)了:


          這就是所謂的熱部署,即是這個(gè)項(xiàng)目把它放在Web容器中之后,我們可以將某些功能給它拿下來(lái),而且拿下來(lái)的時(shí)候不會(huì)對(duì)其他模塊造成影響。


          通過(guò)購(gòu)物網(wǎng)站這個(gè)項(xiàng)目讓大家真實(shí)的感受一下OSGI這個(gè)技術(shù)在項(xiàng)目開(kāi)發(fā)和管理的一些強(qiáng)大的功能。

          想進(jìn)一步了解更多關(guān)于OSGI的知識(shí)可以查看以后的總結(jié)文章。

          轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/acmman/article/details/50848595

          20
          posted @ 2017-08-10 15:57 小馬歌 閱讀(299) | 評(píng)論 (0)編輯 收藏
           
          from:http://www.jianshu.com/p/ccadc2bdb6d7

          第5章 Spring Boot自動(dòng)配置原理

          5.1 SpringBoot的核心組件模塊

          首先,我們來(lái)簡(jiǎn)單統(tǒng)計(jì)一下SpringBoot核心工程的源碼java文件數(shù)量:

          我們cd到spring-boot-autoconfigure工程根目錄下。執(zhí)行

          $ tree | grep -c .java$
          模塊 java文件數(shù)
          spring-boot 551
          spring-boot-actuator 423
          spring-boot-autoconfigure 783
          spring-boot-devtools 169
          spring-boot-cli 180
          spring-boot-tools 355

          我們可以看到有783個(gè)java文件。spring-boot核心工程有551個(gè)java文件。從上面的java文件數(shù)量大致可以看出,SpringBoot技術(shù)框架的核心組成部分:

          spring-boot-autoconfigure spring-boot spring-boot-tools

          我們把SpringBoot源碼導(dǎo)入IntelliJ IDEA,查看artifact的全部依賴(lài)關(guān)系。

          IDEA有個(gè)Maven Projects窗口,一般在右側(cè)能夠找到,如果沒(méi)有可以從菜單欄打開(kāi):View>Tool Windows>Maven Projects;

          選擇要分析的maven module(idea的module相當(dāng)于eclipse的project),右擊show dependencies,會(huì)出來(lái)該module的全部依賴(lài)關(guān)系圖,非常清晰細(xì)致。

          例如,spring-boot-starter-freemarker的依賴(lài)圖分析如下:


          在spring-boot-build 的pom中,我們可以看到:

                     <modules>                 <module>spring-boot-dependencies</module>                 <module>spring-boot-parent</module>                 <module>spring-boot-tools</module>                 <module>spring-boot</module>                 <module>spring-boot-test</module>                 <module>spring-boot-autoconfigure</module>                 <module>spring-boot-test-autoconfigure</module>                 <module>spring-boot-actuator</module>                 <module>spring-boot-devtools</module>                 <module>spring-boot-docs</module>                 <module>spring-boot-starters</module>                 <module>spring-boot-actuator-docs</module>                 <module>spring-boot-cli</module>             </modules>

          其中,在spring-boot-dependencies中,SpringBoot項(xiàng)目維護(hù)了一份龐大依賴(lài)。這些依賴(lài)的版本都是經(jīng)過(guò)實(shí)踐,測(cè)試通過(guò),不會(huì)發(fā)生依賴(lài)沖突的。就這樣一個(gè)事情,就大大減少了Spring開(kāi)發(fā)過(guò)程中,出現(xiàn)jar包沖突的概率。spring-boot-parent依賴(lài)spring-boot-dependencies。

          下面我們簡(jiǎn)要介紹一下SpringBoot子modules。

          spring-boot

          SpringBoot核心工程。

          spring-boot-starters

          是SpringBoot的啟動(dòng)服務(wù)工程。

          spring-boot-autoconfigure

          是SpringBoot實(shí)現(xiàn)自動(dòng)配置的核心工程。

          spring-boot-actuator

          提供SpringBoot應(yīng)用的外圍支撐性功能。 比如:

          • Endpoints,SpringBoot應(yīng)用狀態(tài)監(jiān)控管理
          • HealthIndicator,SpringBoot應(yīng)用健康指示表
          • 提供metrics支持
          • 提供遠(yuǎn)程shell支持

          spring-boot-tools

          提供了SpringBoot開(kāi)發(fā)者的常用工具集。諸如,spring-boot-gradle-plugin,spring-boot-maven-plugin就是這個(gè)工程里面的。

          spring-boot-cli

          是Spring Boot命令行交互工具,可用于使用Spring進(jìn)行快速原型搭建。你可以用它直接運(yùn)行Groovy腳本。如果你不喜歡Maven或Gradle,Spring提供了CLI(Command Line Interface)來(lái)開(kāi)發(fā)運(yùn)行Spring應(yīng)用程序。你可以使用它來(lái)運(yùn)行Groovy腳本,甚至編寫(xiě)自定義命令。

          5.2 SpringBoot Starters

          Spring boot中的starter概念是非常重要的機(jī)制,能夠拋棄以前繁雜的配置,統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要引入starter jar包,spring boot就能自動(dòng)掃描到要加載的信息。

          starter讓我們擺脫了各種依賴(lài)庫(kù)的處理,需要配置各種信息的困擾。Spring Boot會(huì)自動(dòng)通過(guò)classpath路徑下的類(lèi)發(fā)現(xiàn)需要的Bean,并織入bean。

          例如,如果你想使用Spring和用JPA訪問(wèn)數(shù)據(jù)庫(kù),你只要依賴(lài) spring-boot-starter-data-jpa 即可。

          目前,github上spring-boot項(xiàng)目的最新的starter列表spring-boot/spring-boot-starters如下:

          spring-boot-starter spring-boot-starter-activemq spring-boot-starter-actuator spring-boot-starter-amqp spring-boot-starter-aop spring-boot-starter-artemis spring-boot-starter-batch spring-boot-starter-cache spring-boot-starter-cloud-connectors spring-boot-starter-data-cassandra spring-boot-starter-data-couchbase spring-boot-starter-data-elasticsearch spring-boot-starter-data-jpa spring-boot-starter-data-ldap spring-boot-starter-data-mongodb spring-boot-starter-data-mongodb-reactive spring-boot-starter-data-neo4j spring-boot-starter-data-redis spring-boot-starter-data-rest spring-boot-starter-data-solr spring-boot-starter-freemarker spring-boot-starter-groovy-templates spring-boot-starter-hateoas spring-boot-starter-integration spring-boot-starter-jdbc spring-boot-starter-jersey spring-boot-starter-jetty spring-boot-starter-jooq spring-boot-starter-jta-atomikos spring-boot-starter-jta-bitronix spring-boot-starter-jta-narayana spring-boot-starter-log4j2 spring-boot-starter-logging spring-boot-starter-mail spring-boot-starter-mobile spring-boot-starter-mustache spring-boot-starter-parent spring-boot-starter-reactor-netty spring-boot-starter-security spring-boot-starter-social-facebook spring-boot-starter-social-linkedin spring-boot-starter-social-twitter spring-boot-starter-test spring-boot-starter-thymeleaf spring-boot-starter-tomcat spring-boot-starter-undertow spring-boot-starter-validation spring-boot-starter-web spring-boot-starter-web-services spring-boot-starter-webflux spring-boot-starter-websocket

          (源代碼目錄執(zhí)行shell:l|awk '{print $9}', l|awk '{print $9}'|grep -c 'starter')

          共52個(gè)。每個(gè)starter工程里面的pom描述有相應(yīng)的介紹。具體的說(shuō)明,參考官網(wǎng)文檔[1]。關(guān)于這些starters的使用例子,可以參考spring-boot/spring-boot-samples

          比如說(shuō),spring-boot-starter是:

          Core starter, including auto-configuration support, logging and YAML

          這是Spring Boot的核心啟動(dòng)器,包含了自動(dòng)配置、日志和YAML。它的項(xiàng)目依賴(lài)圖如下:



          可以看出,這些starter只是配置,真正做自動(dòng)化配置的代碼的是在spring-boot-autoconfigure里面。同時(shí)spring-boot-autoconfigure依賴(lài)spring-boot工程,這個(gè)spring-boot工程是SpringBoot的核心。

          SpringBoot會(huì)基于你的classpath中的jar包,試圖猜測(cè)和配置您可能需要的bean。

          例如,如果你的classpath中有tomcat-embedded.jar,你可能會(huì)想要一個(gè)TomcatEmbeddedServletContainerFactory Bean (SpringBoot通過(guò)獲取EmbeddedServletContainerFactory來(lái)啟動(dòng)對(duì)應(yīng)的web服務(wù)器。常用的兩個(gè)實(shí)現(xiàn)類(lèi)是TomcatEmbeddedServletContainerFactory和JettyEmbeddedServletContainerFactory)。

          其他的所有基于Spring Boot的starter都依賴(lài)這個(gè)spring-boot-starter。比如說(shuō)spring-boot-starter-actuator的依賴(lài)樹(shù),如下圖:


          5.3 @EnableAutoConfiguration自動(dòng)配置原理

          通過(guò)@EnableAutoConfiguration啟用Spring應(yīng)用程序上下文的自動(dòng)配置,這個(gè)注解會(huì)導(dǎo)入一個(gè)EnableAutoConfigurationImportSelector的類(lèi),而這個(gè)類(lèi)會(huì)去讀取一個(gè)spring.factories下key為EnableAutoConfiguration對(duì)應(yīng)的全限定名的值。

          這個(gè)spring.factories里面配置的那些類(lèi),主要作用是告訴Spring Boot這個(gè)stareter所需要加載的那些xxxAutoConfiguration類(lèi),也就是你真正的要自動(dòng)注冊(cè)的那些bean或功能。然后,我們實(shí)現(xiàn)一個(gè)spring.factories指定的類(lèi),標(biāo)上@Configuration注解,一個(gè)starter就定義完了。

          如果想從自己的starter種讀取應(yīng)用的starter工程的配置,只需要在入口類(lèi)上加上如下注解即可:

          @EnableConfigurationProperties(MyProperties.class)

          讀取spring.factories文件的實(shí)現(xiàn)

          是通過(guò)org.springframework.core.io.support.SpringFactoriesLoader實(shí)現(xiàn)。

          SpringFactoriesLoader的實(shí)現(xiàn)類(lèi)似于SPI(Service Provider Interface,在java.util.ServiceLoader的文檔里有比較詳細(xì)的介紹。java SPI提供一種服務(wù)發(fā)現(xiàn)機(jī)制,為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類(lèi)似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要[3])。

          SpringFactoriesLoader會(huì)加載classpath下所有JAR文件里面的META-INF/spring.factories文件。

          其中加載spring.factories文件的代碼在loadFactoryNames方法里:

          public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";  ....      public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {         String factoryClassName = factoryClass.getName();         try {             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));             List<String> result = new ArrayList<>();             while (urls.hasMoreElements()) {                 URL url = urls.nextElement();                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));                 String factoryClassNames = properties.getProperty(factoryClassName);                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));             }             return result;         }         catch (IOException ex) {             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);         }     }

          通過(guò)org.springframework.boot.autoconfigure.AutoConfigurationImportSelector里面的getCandidateConfigurations方法,獲取到候選類(lèi)的名字List<String>。該方法代碼如下:

              protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,             AnnotationAttributes attributes) {         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());         Assert.notEmpty(configurations,                 "No auto configuration classes found in META-INF/spring.factories. If you "                         + "are using a custom packaging, make sure that file is correct.");         return configurations;     }

          其中,getSpringFactoriesLoaderFactoryClass()方法直接返回的是EnableAutoConfiguration.class, 代碼如下:

              protected Class<?> getSpringFactoriesLoaderFactoryClass() {         return EnableAutoConfiguration.class;     }

          所以,getCandidateConfigurations方法里面的這段代碼:

          List<String> configurations = SpringFactoriesLoader.loadFactoryNames(                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

          會(huì)過(guò)濾出key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對(duì)應(yīng)的值。全限定名都使用如下命名方法:

          包名.外部類(lèi)名 包名.外部類(lèi)名$內(nèi)部類(lèi)名  e.g:  org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

          SpringBoot中的META-INF/spring.factories(完整路徑:spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)中關(guān)于EnableAutoConfiguration的這段配置如下:

          # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAnnotationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

          當(dāng)然了,這些AutoConfiguration不是所有都會(huì)加載的,會(huì)根據(jù)AutoConfiguration上的@ConditionalOnClass等條件,再進(jìn)一步判斷是否加載。我們下文通過(guò)FreeMarkerAutoConfiguration實(shí)例來(lái)分析整個(gè)自動(dòng)配置的過(guò)程。

          5.4 FreeMarkerAutoConfiguration自動(dòng)配置的實(shí)例分析

          我們首先看spring-boot-starter-freemarker工程,目錄結(jié)構(gòu)如下:

          . ├── pom.xml ├── spring-boot-starter-freemarker.iml └── src     └── main         └── resources             └── META-INF                 └── spring.provides  4 directories, 3 files

          我們可以看出,這個(gè)工程沒(méi)有任何Java代碼,只有兩個(gè)文件:pom.xml跟spring.provides。starter本身在你的應(yīng)用程序中實(shí)際上是空的。

          其中,
          spring.provides文件

          provides: freemarker,spring-context-support

          主要是給這個(gè)starter起個(gè)好區(qū)分的名字。

          Spring Boot 通過(guò)starter對(duì)項(xiàng)目的依賴(lài)進(jìn)行統(tǒng)一管理. starter利用了maven的傳遞依賴(lài)解析機(jī)制,把常用庫(kù)聚合在一起, 組成了針對(duì)特定功能而定制的依賴(lài)starter。

          我們可以使用IDEA提供的maven依賴(lài)圖分析的功能(如下圖),得到spring-boot-starter-freemarker依賴(lài)的module。


          IDEA提供的maven依賴(lài)圖分析

          spring-boot-starter-freemarker依賴(lài)的module

          從上面的依賴(lài)圖,我們可以清晰看出其間依賴(lài)關(guān)系。

          當(dāng)Spring Boot Application中自動(dòng)配置EnableAutoConfiguration的相關(guān)類(lèi)執(zhí)行完畢之后,Spring Boot會(huì)進(jìn)一步解析對(duì)應(yīng)類(lèi)的配置信息。如果我們配置了spring-boot-starter-freemarker ,maven就會(huì)通過(guò)這個(gè)starter所依賴(lài)的spring-boot-autoconfigure,自動(dòng)傳遞到spring-boot-autoconfigure工程中。

          我們來(lái)簡(jiǎn)單分析一下spring-boot-autoconfigure工程的架構(gòu)。

          其中,F(xiàn)reeMarker的自動(dòng)配置類(lèi)是org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration。

          下面我們來(lái)簡(jiǎn)要分析一下FreeMarkerAutoConfiguration這個(gè)類(lèi)。

          在FreeMarkerAutoConfiguration類(lèi)上面有四行注解:

          @Configuration @ConditionalOnClass({ freemarker.template.Configuration.class,         FreeMarkerConfigurationFactory.class }) @AutoConfigureAfter(WebMvcAutoConfiguration.class) @EnableConfigurationProperties(FreeMarkerProperties.class) public class FreeMarkerAutoConfiguration {     ... }

          其中,
          (1)@Configuration,是org.springframework.context.annotation包里面的注解。這么說(shuō)吧,用@Configuration注解該類(lèi),等價(jià) 與XML中配置beans;用@Bean標(biāo)注方法等價(jià)于XML中配置bean。

          (2)@ConditionalOnClass,org.springframework.boot.autoconfigure.condition包里面的注解。意思是當(dāng)類(lèi)路徑下有指定的類(lèi)的條件下,才會(huì)去注冊(cè)被標(biāo)注的類(lèi)為一個(gè)bean。在上面的代碼中的意思就是,當(dāng)類(lèi)路徑中有freemarker.template.Configuration.class,FreeMarkerConfigurationFactory.class兩個(gè)類(lèi)的時(shí)候,才會(huì)實(shí)例化FreeMarkerAutoConfiguration這個(gè)Bean。

          (3)@AutoConfigureAfter,org.springframework.boot.autoconfigure包里面的注解。這個(gè)通過(guò)注解的名字意思就可以知道,當(dāng)WebMvcAutoConfiguration.class這個(gè)類(lèi)實(shí)例化完畢,才能實(shí)例化FreeMarkerAutoConfiguration(有個(gè)先后順序)。SpringBoot使用@ AutoConfigureBefore、@AutoConfigureAfter注解來(lái)定義這些配置類(lèi)的載入順序。

          (4)@EnableConfigurationProperties,表示啟動(dòng)對(duì)FreeMarkerProperties.class的內(nèi)嵌配置支持,自動(dòng)將FreeMarkerProperties注冊(cè)為一個(gè)bean。這個(gè)FreeMarkerProperties類(lèi)里面就是關(guān)于FreeMarker屬性的配置:

          @ConfigurationProperties(prefix = "spring.freemarker") public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {      public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";      public static final String DEFAULT_PREFIX = "";      public static final String DEFAULT_SUFFIX = ".ftl";      /**      * Well-known FreeMarker keys which will be passed to FreeMarker's Configuration.      */     private Map<String, String> settings = new HashMap<>();      /**      * Comma-separated list of template paths.      */     private String[] templateLoaderPath = new String[] { DEFAULT_TEMPLATE_LOADER_PATH };      /**      * Prefer file system access for template loading. File system access enables hot      * detection of template changes.      */     private boolean preferFileSystemAccess = true;      public FreeMarkerProperties() {         super(DEFAULT_PREFIX, DEFAULT_SUFFIX);     }      public Map<String, String> getSettings() {         return this.settings;     }      public void setSettings(Map<String, String> settings) {         this.settings = settings;     }      public String[] getTemplateLoaderPath() {         return this.templateLoaderPath;     }      public boolean isPreferFileSystemAccess() {         return this.preferFileSystemAccess;     }      public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {         this.preferFileSystemAccess = preferFileSystemAccess;     }      public void setTemplateLoaderPath(String... templateLoaderPaths) {         this.templateLoaderPath = templateLoaderPaths;     }  }

          綜上,當(dāng)(1)(2)兩個(gè)條件滿足時(shí),才會(huì)繼續(xù)(3)(4)的動(dòng)作,同時(shí)注冊(cè)FreeMarkerAutoConfiguration這個(gè)Bean。該類(lèi)的結(jié)構(gòu)如下圖:


          我們來(lái)看其內(nèi)部類(lèi)FreeMarkerWebConfiguration的代碼:

              @Configuration     @ConditionalOnClass(Servlet.class)     @ConditionalOnWebApplication(type = Type.SERVLET)     public static class FreeMarkerWebConfiguration extends FreeMarkerConfiguration {          @Bean         @ConditionalOnMissingBean(FreeMarkerConfig.class)         public FreeMarkerConfigurer freeMarkerConfigurer() {             FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();             applyProperties(configurer);             return configurer;         }          @Bean         public freemarker.template.Configuration freeMarkerConfiguration(                 FreeMarkerConfig configurer) {             return configurer.getConfiguration();         }          @Bean         @ConditionalOnMissingBean(name = "freeMarkerViewResolver")         @ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)         public FreeMarkerViewResolver freeMarkerViewResolver() {             FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();             this.properties.applyToViewResolver(resolver);             return resolver;         }          @Bean         @ConditionalOnMissingBean         @ConditionalOnEnabledResourceChain         public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {             return new ResourceUrlEncodingFilter();         }      }

          其中,
          (1)@ConditionalOnWebApplication(type = Type.SERVLET), 是當(dāng)該應(yīng)用是基于Servlet的Web應(yīng)用時(shí)。

          (2)@ConditionalOnMissingBean(name = "freeMarkerViewResolver"),是當(dāng)Spring容器中不存在freeMarkerViewResolver的Bean時(shí)。

          (3)@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true),指定的spring.freemarker.enabled屬性是否有。如果沒(méi)有(IfMissing),設(shè)為true。

          當(dāng)(1)(2)(3)三個(gè)條件都滿足,則注冊(cè)freeMarkerViewResolver這個(gè)Bean。

          我們也可以自定義我們自己的my-starter,以及實(shí)現(xiàn)對(duì)應(yīng)的@MyEnableAutoConfiguration。SpringBoot有很多第三方starter,其自動(dòng)配置的原理基本都是這樣,比如mybatis-spring-boot-starter的MybatisAutoConfiguration,閱讀源碼https://github.com/mybatis/spring-boot-starter[4]

          上面文字描述了這么多,再用一張形象生動(dòng)的圖來(lái)說(shuō)明[5]:


          SpringBoot Autoconfigure 工作原理圖

          5.5 spring.factories與定義應(yīng)用程序的初始化行為

          上面說(shuō)了這么多,講的都是讀取properties文件中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對(duì)應(yīng)的值。SpringBoot內(nèi)部還有許多其他的key用于過(guò)濾得到需要加載的類(lèi)。

          # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer  # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer  # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener  # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition  # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer  # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

          這些key仍然是定義在spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories文件中。

          還有對(duì)應(yīng)的用于測(cè)試的自動(dòng)配置,在
          spring-boot/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories文件中定義。

          另外,我們使用spring.factories里還可以定制應(yīng)用程序的初始化行為。這樣我們就可以在應(yīng)用程序載入前操縱Spring的應(yīng)用程序上下文ApplicationContext。

          例如,可以使用ConfigurableApplicationContext類(lèi)的addApplicationListener()方法,在應(yīng)用上下文ApplicationContext中創(chuàng)建監(jiān)聽(tīng)器。

          自動(dòng)配置運(yùn)行日志報(bào)告功能就是這么實(shí)現(xiàn)的。我們來(lái)看在spring.factories中,Initializers一段的配置:

          # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

          其中,AutoConfigurationReportLoggingInitializer監(jiān)聽(tīng)到系統(tǒng)事件時(shí),比如上下文刷新ContextRefreshedEvent或應(yīng)用程序啟動(dòng)故障ApplicationFailedEvent之類(lèi)的事件,Spring Boot可以做一些事情。這里說(shuō)的代碼在AutoConfigurationReportLoggingInitializer.AutoConfigurationReportListener里面。關(guān)于支持的事件類(lèi)型supportsEventType的如下:

              private class AutoConfigurationReportListener implements GenericApplicationListener {  ...         @Override         public boolean supportsEventType(ResolvableType resolvableType) {             Class<?> type = resolvableType.getRawClass();             if (type == null) {                 return false;             }             return ContextRefreshedEvent.class.isAssignableFrom(type)                     || ApplicationFailedEvent.class.isAssignableFrom(type);         }          @Override         public boolean supportsSourceType(Class<?> sourceType) {             return true;         }          @Override         public void onApplicationEvent(ApplicationEvent event) {     AutoConfigurationReportLoggingInitializer.this.onApplicationEvent(event);         }      }

          要以調(diào)試模式啟動(dòng)應(yīng)用程序,可以使用-Ddebug標(biāo)識(shí),或者在application.properties文件這添加屬性debug= true。這樣,當(dāng)我們以調(diào)試模式啟動(dòng)應(yīng)用程序時(shí),SpringBoot就可以幫助我們創(chuàng)建自動(dòng)配置的運(yùn)行報(bào)告。對(duì)于每個(gè)自動(dòng)配置,通過(guò)報(bào)告我們可以看到它啟動(dòng)或失敗的原因。 這個(gè)報(bào)告內(nèi)容格式大致如下:

          ========================= AUTO-CONFIGURATION REPORT =========================   Positive matches: -----------------     DataSourceAutoConfiguration matched:       - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)     DataSourceAutoConfiguration#dataSourceInitializer matched:       - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer; SearchStrategy: all) did not find any beans (OnBeanCondition)     DataSourceAutoConfiguration.PooledDataSourceConfiguration matched:       - AnyNestedCondition 2 matched 0 did not; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.PooledDataSourceAvailable PooledDataSource found supported DataSource; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.ExplicitType @ConditionalOnProperty (spring.datasource.type) matched (DataSourceAutoConfiguration.PooledDataSourceCondition)       - @ConditionalOnMissingBean (types: javax.sql.DataSource,javax.sql.XADataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)     ...  Exclusions: -----------      None   Unconditional classes: ----------------------      org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration      org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration      org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration      org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration

          除了SpringBoot官方提供的starter外,還有社區(qū)貢獻(xiàn)的很多常用的第三方starter,列表可參考[2]。

          另外,國(guó)內(nèi)很多公司使用RPC框架dubbo,關(guān)于SpringBoot集成dubbo,可參考:https://github.com/linux-china/spring-boot-dubbo。

          參考資料:

          1.http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter
          2.https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters
          3.http://www.cnblogs.com/javaee6/p/3714719.html
          4.https://github.com/mybatis/spring-boot-starter
          5.https://afoo.me/posts/2015-07-09-how-spring-boot-works.html



          作者:華夏商周秦漢唐宋元明清中華民國(guó)
          鏈接:http://www.jianshu.com/p/ccadc2bdb6d7
          來(lái)源:簡(jiǎn)書(shū)
          著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
          posted @ 2017-08-02 16:35 小馬歌 閱讀(1745) | 評(píng)論 (0)編輯 收藏
           
               摘要: from:http://blog.csdn.net/lylwo317/article/details/52163304序言注解在Java中到底是什么樣的東西?具體是如何實(shí)現(xiàn)的? 本文將一層一層深入探究注解的實(shí)現(xiàn)原理。為了盡可能的將分析的過(guò)程呈現(xiàn)出來(lái),所以文章包含了大量的截圖和代碼。(ps:如果圖片看不清楚,請(qǐng)將網(wǎng)頁(yè)放大來(lái)看,chrome可以通過(guò)ctrl+鼠標(biāo)滾輪放大)前期準(zhǔn)備知識(shí)方面開(kāi)始...  閱讀全文
          posted @ 2017-08-02 11:15 小馬歌 閱讀(598) | 評(píng)論 (1)編輯 收藏
           

          如果沒(méi)有用來(lái)讀取注解的方法和工作,那么注解也就不會(huì)比注釋更有用處了。使用注解的過(guò)程中,很重要的一部分就是創(chuàng)建于使用注解處理器。Java SE5擴(kuò)展了反射機(jī)制的API,以幫助程序員快速的構(gòu)造自定義注解處理器。


          注解處理器類(lèi)庫(kù)(java.lang.reflect.AnnotatedElement):

            Java使用Annotation接口來(lái)代表程序元素前面的注解,該接口是所有Annotation類(lèi)型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受注解的程序元素,該接口主要有如下幾個(gè)實(shí)現(xiàn)類(lèi):

            Class:類(lèi)定義
            Constructor:構(gòu)造器定義
            Field:累的成員變量定義
            Method:類(lèi)的方法定義
            Package:類(lèi)的包定義

            java.lang.reflect 包下主要包含一些實(shí)現(xiàn)反射功能的工具類(lèi),實(shí)際上,java.lang.reflect 包所有提供的反射API擴(kuò)充了讀取運(yùn)行時(shí)Annotation信息的能力。當(dāng)一個(gè)Annotation類(lèi)型被定義為運(yùn)行時(shí)的Annotation后,該注解才能是運(yùn)行時(shí)可見(jiàn),當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會(huì)被虛擬機(jī)讀取。
            AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過(guò)反射獲取了某個(gè)類(lèi)的AnnotatedElement對(duì)象之后,程序就可以調(diào)用該對(duì)象的如下四個(gè)個(gè)方法來(lái)訪問(wèn)Annotation信息:

            方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定類(lèi)型的注解,如果該類(lèi)型注解不存在,則返回null。
            方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
            方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類(lèi)型的注解,存在則返回true,否則返回false.
            方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒(méi)有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。

            一個(gè)簡(jiǎn)單的注解處理器:  

          復(fù)制代碼
          /***********注解聲明***************/  /**  * 水果名稱(chēng)注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName {     String value() default ""; }  /**  * 水果顏色注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor {     /**      * 顏色枚舉      * @author peida      *      */     public enum Color{ BULE,RED,GREEN};          /**      * 顏色屬性      * @return      */     Color fruitColor() default Color.GREEN;  }  /**  * 水果供應(yīng)者注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider {     /**      * 供應(yīng)商編號(hào)      * @return      */     public int id() default -1;          /**      * 供應(yīng)商名稱(chēng)      * @return      */     public String name() default "";          /**      * 供應(yīng)商地址      * @return      */     public String address() default ""; }  /***********注解使用***************/  public class Apple {          @FruitName("Apple")     private String appleName;          @FruitColor(fruitColor=Color.RED)     private String appleColor;          @FruitProvider(id=1,name="陜西紅富士集團(tuán)",address="陜西省西安市延安路89號(hào)紅富士大廈")     private String appleProvider;          public void setAppleColor(String appleColor) {         this.appleColor = appleColor;     }     public String getAppleColor() {         return appleColor;     }          public void setAppleName(String appleName) {         this.appleName = appleName;     }     public String getAppleName() {         return appleName;     }          public void setAppleProvider(String appleProvider) {         this.appleProvider = appleProvider;     }     public String getAppleProvider() {         return appleProvider;     }          public void displayName(){         System.out.println("水果的名字是:蘋(píng)果");     } }  /***********注解處理器***************/  public class FruitInfoUtil {     public static void getFruitInfo(Class<?> clazz){                  String strFruitName=" 水果名稱(chēng):";         String strFruitColor=" 水果顏色:";         String strFruitProvicer="供應(yīng)商信息:";                  Field[] fields = clazz.getDeclaredFields();                  for(Field field :fields){             if(field.isAnnotationPresent(FruitName.class)){                 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);                 strFruitName=strFruitName+fruitName.value();                 System.out.println(strFruitName);             }             else if(field.isAnnotationPresent(FruitColor.class)){                 FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);                 strFruitColor=strFruitColor+fruitColor.fruitColor().toString();                 System.out.println(strFruitColor);             }             else if(field.isAnnotationPresent(FruitProvider.class)){                 FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);                 strFruitProvicer=" 供應(yīng)商編號(hào):"+fruitProvider.id()+" 供應(yīng)商名稱(chēng):"+fruitProvider.name()+" 供應(yīng)商地址:"+fruitProvider.address();                 System.out.println(strFruitProvicer);             }         }     } }  /***********輸出結(jié)果***************/ public class FruitRun {      /**      * @param args      */     public static void main(String[] args) {                  FruitInfoUtil.getFruitInfo(Apple.class);              }  }  ====================================  水果名稱(chēng):Apple  水果顏色:RED  供應(yīng)商編號(hào):1 供應(yīng)商名稱(chēng):陜西紅富士集團(tuán) 供應(yīng)商地址:陜西省西安市延安路89號(hào)紅富士大廈
          復(fù)制代碼

             Java注解的基礎(chǔ)知識(shí)點(diǎn)(見(jiàn)下面導(dǎo)圖)基本都過(guò)了一遍,下一篇我們通過(guò)設(shè)計(jì)一個(gè)基于注解的簡(jiǎn)單的ORM框架,來(lái)綜合應(yīng)用和進(jìn)一步加深對(duì)注解的各個(gè)知識(shí)點(diǎn)的理解和運(yùn)用。

           

           


          分類(lèi): java
          posted @ 2017-08-02 11:07 小馬歌 閱讀(233) | 評(píng)論 (0)編輯 收藏
           
          http://blog.csdn.net/coslay/article/details/43458907
          1. 我們需要知道的是找到默認(rèn)值的方法,掌握默認(rèn)值的大概量級(jí),在不同的版本下哪些是常用的默認(rèn)參數(shù),哪些是必須設(shè)置的參數(shù),哪些是可以選擇的、盡量不要去碰的設(shè)置。  
          1. 參數(shù)設(shè)置同一個(gè)類(lèi)型會(huì)有多種參數(shù),而且都有默認(rèn)值,大家不要混用噢,混用的結(jié)果很多時(shí)候難以預(yù)料,雖然在某種情況下可能得到了一個(gè)測(cè)試結(jié)果,但如果你沒(méi)有真正了解JVM的內(nèi)核源碼,是不可能知道所有細(xì)節(jié)的,即測(cè)試結(jié)果不能當(dāng)成任何場(chǎng)景下的一個(gè)結(jié)論,只能作為一種參考。  

          本篇文章基于Java 6update 21oder 21之后)版本, HotSpot JVM 提供給了兩個(gè)新的參數(shù),在JVM啟動(dòng)后,在命令行中可以輸出所有XX參數(shù)和值。

          1. -XX:+PrintFlagsFinal and -XX:+PrintFlagsInitial  

          讓我們現(xiàn)在就了解一下新參數(shù)的輸出。以 -client 作為參數(shù)的 -XX:+PrintFlagsFinal   的結(jié)果是一個(gè)按字母排序的590個(gè)參數(shù)表格(注意,每個(gè)release版本參數(shù)的數(shù)量會(huì)不一樣)

          1. $ java -client -XX:+PrintFlagsFinal Benchmark  
          2. [Global flags]  
          3. uintx AdaptivePermSizeWeight               = 20               {product}  
          4. uintx AdaptiveSizeDecrementScaleFactor     = 4                {product}  
          5. uintx AdaptiveSizeMajorGCDecayTimeScale    = 10               {product}  
          6. uintx AdaptiveSizePausePolicy              = 0                {product}[...]  
          7. uintx YoungGenerationSizeSupplementDecay   = 8                {product}  
          8. uintx YoungPLABSize                        = 4096             {product}  
          9.  bool ZeroTLAB                             = false            {product}  
          10.  intx hashCode                             = 0                {product}  

          (校對(duì)注:你可以嘗試在命令行輸入上面的命令,親自實(shí)現(xiàn)下)

          表格的每一行包括五列,來(lái)表示一個(gè)XX參數(shù)。第一列表示參數(shù)的數(shù)據(jù)類(lèi)型,第二列是名稱(chēng),第四列為值,第五列是參數(shù)的類(lèi)別。第三列”=”表示第四列是參數(shù)的默認(rèn)值,而”:=” 表明了參數(shù)被用戶(hù)或者JVM賦值了。

          注意對(duì)于這個(gè)例子我只是用了Benchmark類(lèi),因?yàn)檫@個(gè)系列前面的章節(jié)也是用的這個(gè)類(lèi)。甚至沒(méi)有一個(gè)主類(lèi)的情況下你能得到相同的輸出,通過(guò)運(yùn)行Java 帶另外的參數(shù) -version.現(xiàn)在讓我們檢查下 server VM提供了多少個(gè)參數(shù)。我們也能指定參數(shù)-XX:+UnlockExperimentalVMOptions 和-XX:+UnlockDiagnosticVMOptions ;來(lái)解鎖任何額外的隱藏參數(shù)。

          1. $ java -server -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal Benchmark  
          724個(gè)參數(shù),讓我們看一眼那些已經(jīng)被賦值的參數(shù)。
          1. $ java -server -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal Benchmark | grep ":"  
          2. uintx InitialHeapSize                     := 57505088         {product}  
          3. uintx MaxHeapSize                         := 920649728        {product}  
          4. uintx ParallelGCThreads                   := 4                {product}  
          5.  bool PrintFlagsFinal                     := true             {product}  
          6.  bool UseParallelGC                       := true             {product}  

          (校對(duì)注:這個(gè)命令非常有用)我們僅設(shè)置一個(gè)自己的參數(shù) -XX:+PrintFlagsFinal。其他參數(shù)通過(guò)server VM基于系統(tǒng)設(shè)置的,以便以合適的堆大小和GC設(shè)置運(yùn)行。

          如果我們只想看下所有XX參數(shù)的默認(rèn)值,能夠用一個(gè)相關(guān)的參數(shù),-XX:+PrintFlagsInitial  。 用 -XX:+PrintFlagsInitial, 只是展示了第三列為“=”的數(shù)據(jù)(也包括那些被設(shè)置其他值的參數(shù))。

          然而,注意當(dāng)與-XX:+PrintFlagsFinal 對(duì)比的時(shí)候,一些參數(shù)會(huì)丟失,大概因?yàn)檫@些參數(shù)是動(dòng)態(tài)創(chuàng)建的。

          研究表格的內(nèi)容是很有意思的,通過(guò)比較client和server VM的行為,很明顯了解哪些參數(shù)會(huì)影響其他的參數(shù)。有興趣的讀者,可以看一下這篇不錯(cuò)文章Inspecting HotSpot JVM Options。這個(gè)文章主要解釋了第五列的參數(shù)類(lèi)別。

          -XX:+PrintCommandLineFlags

          讓我們看下另外一個(gè)參數(shù),事實(shí)上這個(gè)參數(shù)非常有用: -XX:+PrintCommandLineFlags。這個(gè)參數(shù)讓JVM打印出那些已經(jīng)被用戶(hù)或者JVM設(shè)置過(guò)的詳細(xì)的XX參數(shù)的名稱(chēng)和值。

          換句話說(shuō),它列舉出 -XX:+PrintFlagsFinal的結(jié)果中第三列有":="的參數(shù)。以這種方式,我們可以用-XX:+PrintCommandLineFlags作為快捷方式來(lái)查看修改過(guò)的參數(shù)。看下面的例子。

          1. $ java -server -XX:+PrintCommandLineFlags Benchmark   

          1. -XX:InitialHeapSize=57505088 -XX:MaxHeapSize=920081408 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseParallelGC  

          現(xiàn)在如果我們每次啟動(dòng)java 程序的時(shí)候設(shè)置 -XX:+PrintCommandLineFlags 并且輸出到日志文件上,這樣會(huì)記錄下我們?cè)O(shè)置的JVM 參數(shù)對(duì)應(yīng)用程序性能的影響。類(lèi)似于 -showversion(見(jiàn) Part1),我建議 –XX:+PrintCommandLineFlags 這個(gè)參數(shù)應(yīng)該總是設(shè)置在JVM啟動(dòng)的配置項(xiàng)里。因?yàn)槟銖牟恢滥闶裁磿r(shí)候會(huì)需要這些信息。

          奇怪的是在這個(gè)例子中,通過(guò) -XX:+PrintCommandLineFlags 列出堆的最大值會(huì)比通過(guò)-XX:+PrintFlagsFinal列舉出的相應(yīng)值小一點(diǎn)。如果誰(shuí)知道兩者之間不同的原因,請(qǐng)告訴我。

          轉(zhuǎn)載自:http://ifeve.com/useful-jvm-flags-part-3-printing-all-xx-flags-and-their-values/



             Java程序員有時(shí)候需要了解JVM相關(guān)的參數(shù),不管是出于好奇或者工作需要。Oracle的文檔中列出了一些,(點(diǎn)擊這里),單并不是全部,而且有些參數(shù)的設(shè)置會(huì)默認(rèn)啟用或者關(guān)閉其他一些參數(shù),而在某些情況下設(shè)置某個(gè)參數(shù)是不會(huì)生效的。還有些時(shí)候你想讓JVM做某些事情,但是你不知道那個(gè)參數(shù)可以用。下面介紹一些辦法用以列出所有參數(shù),這樣你在研究或者Google的時(shí)候也比較有明確的目標(biāo)。

              如果你想查看一下線上正在運(yùn)行的JVM到底設(shè)置了那些參數(shù),生效的是那些,可能用到的方法:

              1. 在Linux下用ps命令找到啟動(dòng)Java應(yīng)用時(shí)的參數(shù)

          1. ps -ef | grep "your java app name"    

              這個(gè)命令會(huì)打出你啟動(dòng)Java應(yīng)用時(shí)傳給java命令的所有參數(shù),你可以看到里面的JVM參數(shù)。

              2.直接看啟動(dòng)腳本,或者參數(shù)配置

              你未必能找到所有設(shè)置這JVM參數(shù)的地方,容易遺漏。

              一般來(lái)講以上兩種辦法都需要對(duì)JVM了如指掌或者非常熟悉,至少對(duì)特定的參數(shù)。

              

              其實(shí)JVM中有一個(gè)參數(shù)-XX:+PrintFlagsFinal,可以打印出幾乎所有的JVM支持的參數(shù)以及他們的默認(rèn)值。如果你想要查看你的Java應(yīng)用到底使用了那些參數(shù),只要在啟動(dòng)的時(shí)候加上這個(gè)參數(shù)就可以了。

              1.查看你使用的JDK支持的參數(shù)

          1. java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version    

              2.打印Java應(yīng)用啟用的JVM參數(shù)

          1. java -XX:+PrintFlagsFinal -classpath=/path/to/your/libs MainClass    

              3.如果你的Java應(yīng)用已經(jīng)是運(yùn)行狀態(tài)了,你想查看某個(gè)JVM參數(shù)生效沒(méi)有可以使用jinfo這個(gè)工具。比如說(shuō)大名鼎鼎的G1垃圾回收器,在JDK7update3中不論是客戶(hù)端(-client)還是服務(wù)器(-server)模式下都不是默認(rèn)啟動(dòng)的。

              jinfo是隨JDK一起發(fā)布的,使用時(shí)先用jps找到Java應(yīng)用的pid。直接運(yùn)行jinfo可以查看使用說(shuō)明。

          1. jinfo -flag UseParallelOldGC 31072    

              3.如果你的Java應(yīng)用已經(jīng)是運(yùn)行狀態(tài)了,你想查看某個(gè)JVM參數(shù)生效沒(méi)有可以使用jinfo這個(gè)工具。比如說(shuō)大名鼎鼎的G1垃圾回收器,在JDK7update3中不論是客戶(hù)端(-client)還是服務(wù)器(-server)模式下都不是默認(rèn)啟動(dòng)的。

              jinfo是隨JDK一起發(fā)布的,使用時(shí)先用jps找到Java應(yīng)用的pid。直接運(yùn)行jinfo可以查看使用說(shuō)明。

          1. jinfo -flag UseParallelOldGC 31072  <span style="color: rgb(0, 204, 0); line-height: 18px; font-family: Consolas, 'Courier New', Courier, mono, serif; background-color: inherit;"> </span>  
          1. -XX:+UseParallelOldGC    
              JDK中實(shí)用的工具還很多,可以逐個(gè)的體驗(yàn)一下${JAVA_HOME}/bin目錄中的每個(gè)命令,有驚喜。


          參考:http://blog.csdn.net/redhat456/article/details/7360249

          0
          posted @ 2017-07-28 11:01 小馬歌 閱讀(431) | 評(píng)論 (0)編輯 收藏
          僅列出標(biāo)題
          共95頁(yè): 上一頁(yè) 1 2 3 4 5 6 7 8 9 下一頁(yè) Last 
           
          主站蜘蛛池模板: 喀喇沁旗| 紫阳县| 尼勒克县| 囊谦县| 来安县| 柯坪县| 潮安县| 固始县| 满城县| 绥棱县| 阿拉善盟| 历史| 历史| 四川省| 筠连县| 东安县| 海口市| 泸定县| 望城县| 沈丘县| 靖江市| 龙胜| 吉隆县| 达州市| 陆川县| 湖州市| 富蕴县| 鄱阳县| 咸阳市| 阿拉善盟| 永川市| 宣城市| 商城县| 永寿县| 运城市| 上栗县| 汕头市| 固原市| 彰武县| 竹山县| 临沭县|