沒有Maven之前的日子
個人的一個小感受,學習一個新技術,應該以歷史的眼光開看待這個新技術出現的原因,以及幫我們解決了什么問題。我們來回憶一下沒有Maven的日子是怎么樣的?
- 開發一個項目,需要用別人寫好的jar包,我們先把開源的jar包下載下來放到項目的lib目錄下,并把這個目錄添加到CLASSPATH(告訴JAVA執行環境,在哪些目錄下可以找到你要執行的Java程序需要的類或者包)
- 我們下載了a.jar發現a.jar還需要依賴b.jar,結果又去把b.jar包下載下來開始運行
- 如果運氣夠好,我們的項目在添加完所有的依賴后,能正產運行了。如果運氣差點,還會遇到版本的問題,例如a.jar在調用b.jar的時候發現b.jar根本沒有這個方法,在別的版本中才有,現在好了,光找依賴和適配版本就能花上不少時間
- 而且我們往git上上傳代碼的時候,還必須把這些lib都上傳上去。別人下載我們的代碼時也必須把lib下載下來,這個真心耗費時間
這時候Maven作為Java世界的包管理工具出現了,當然Java世界還有其他包管理工具,例如gradle等。就像yum是linux世界的包管理工具,webpack是前端世界的包管理工具一樣
Maven倉庫的種類
Maven找jar包的過程是這樣的,先在本地倉庫找,找不到再去私服(如果配置了的話),再找不到去中央倉庫(
http://repo1.maven.org/maven2/,maven團隊負責維護)
從中央倉庫找到后,會在私服和本地倉庫放一份,從私服找到后也會在本地倉庫放一份
當你安裝在好了Maven以后,在conf目錄下有個settings.xml文件,這個里面配置的項很多,后文會詳細介紹這個配置文件。
<!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository> -->
在這個配置文件下有這樣一段話,說了Maven默認的本地倉庫地址為${user.home}/.m2/repository(當然你可以重新設置本地倉庫的地址,上面就是模板),我是window電腦,來看看這個目錄
看到有很多jar包被存到本地,當然如果你想配置私服也是在settings.xml上進行配置,隨便一搜很多教程,不再贅述
搭建私服好處多多,在一個公司內部可以開發一些公共的基礎組件放到私服上,方便其他同事使用
Maven的默認配置
一個Maven的項目的整體結構是這樣的
在這里插入圖片描述
為什么一個Maven項目的文件結構是這種的呢?
這就不得不說到Maven的一個特性,約定優于配置。
Maven默認配置了${project.basedir}/src/main/java為項目的源代碼目錄
${project.basedir}/src/main/test為項目的測試代碼目錄
${project.basedir}/target為項目的編譯輸出目錄等
spring boot就是約定優于配置的體現,想想我們用spring mvc的時候還得配置視圖解析器,包的自動掃描,而用了spring boot框架,我們就完全不用再配置了
Maven項目詳解
安裝還是挺簡單的,我就不再介紹,我也沒有單獨下載,一般就用了Idea自帶的Maven了,下載完后目錄結構如下:
bin目錄:
該目錄包含了mvn運行的腳本,這些腳本用來配置java命令,準備好classpath和相關的Java系統屬性,然后執行Java命令。
boot目錄:
該目錄只包含一個文件,該文件為
plexus-classworlds-2.5.2.jar。plexus-classworlds是一個類加載器框架,相對于默認的java類加載器,它提供了更加豐富的語法以方便配置,Maven使用該框架加載自己的類庫。
conf目錄:
該目錄包含了一個非常重要的文件settings.xml。直接修改該文件,就能在機器上全局地定制maven的行為,即對所有用戶都生效。一般情況下,我們更偏向于復制該文件至~/.m2/目錄下(~表示用戶家目錄,windows下~就是C:UsersPeng,Peng是小編的用戶名),然后修改該文件,在用戶級別定制Maven的行為。
lib目錄:
該目錄包含了所有Maven運行時需要的Java類庫,Maven本身是分模塊開發的,因此用戶能看到諸如maven-core-3.0.jar、maven-model-3.0.jar之類的文件,此外這里還包含一些Maven用到的第三方依賴如commons-cli-1.2.jar、commons-lang-2.6.jar等等。、
settings.xml配置文件詳解
我們來詳細說一下settings.xml這個文件,這個文件可以定制Maven的行為,上面已經說到settings.xml可以放在2個位置,~/.m2/setting.xml(默認沒有,需要我們自己復制)和${maven.home}/conf/setting.xml
這2個配置文件的加載順序為~/.m2/setting.xml>${maven.home}/conf/setting.xml,為了不影響他人,所以我們將conf下的settings.xml復制到家目錄,在用戶級別定制Maven的行為。
這個和配置環境變量有點類似,Windos和Linux都可以配置系統級別的環境變量和用戶級別的環境變量,這里單說一下Linux的吧,在/etc/profile里面配置的就是系統級別的環境變量,在~/.bash_profile里面配置的就是用戶級別的環境變量
各種配置項還是挺多的,設置鏡像倉庫(國內用阿里云的比較多),設置代理,不再贅述
maven常用命令
當然也可以連著使用
mvn clean package 清理打包
mvn clean package -DskipTests=true 清理打包,并跳過測試用例
mvn clean install 清理打包,并將jar包或者war包復制到本地倉庫
運行單測的時候也沒必要一個一個點測試方法,mvn test 一個命令跑完所有測試用例,
要注意的是只會執行以Test開頭或者結尾的測試類,也沒必要自己寫測試類,我在推薦閱讀第一篇文章中演示了快速生成測試類的方法,可以去看看,生成的測試類都是以Test結尾的
mvn dependency:tree > show.txt 將依賴輸出重定向到文件中,方便查看
pom.xml詳解
groupId 公司域名倒過來
artifactId 功能命名
version 版本號
這三個維度確定一個jar包,就像用(x,y,z)坐標在三維空間中唯一確定一個點。
packaging 打包方式,jar,war,maven-plugin(開發maven插件)
scope詳解
參數解釋是否會被打入最終的jar包compile默認的scope是test測試使用否provided編譯需要否runtime編譯不需要,運行時需要(接口與實現分離)是system加載本地jar否
類似如下這種,沒有指定scope,說明scope是compile
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
test是指在運行測試用例的時候才會用到,沒必要打入到最后的jar里面,所以你看到的測試框架的scope基本上都是test
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
provided,編譯的時候會用到,但不會被打入最后的jar包
例如想把spring boot項目以war包的形式放在Tomcat中運行,首先得加入如下依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
或者你寫了一個放在Storm集群或者Flink集群上運行的任務,最后都要把Storm的依賴或者Flink的依賴設置成provided,因為集群上已經都有這些環境的jar包、
如果你用到lombok插件的話,你會發現lombok的Maven是如下形式,說明它只會編譯的時候會用到。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> <scope>provided</scope> </dependency>
我寫了如下一個測試類
@Data public class Test { private String name; private int age; }
生成的class文件反編譯后的如下,驗證了我們的想法,編譯之后確實沒有必要再用lombok這個jar包
public class Test { private String name; private int age; public Test() { } public String getName() { return this.name; } public int getAge() { return this.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } }
runtime,運行時才會用到。例如,如果你的項目有對數據庫的操作,但沒有加入相應的JDBC的實現jar包,如MySQL-connector-java,是可以編譯成功的,只有運行時才會報錯。所以你看到的JDBC實現的jar包scope為runtime,表明這個jar包在運行時才會用到
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> <scope>runtime</scope> </dependency>
system,本地加載jar,當你和第三方公司合作,他們只是給了你一個jar包時,你可以有三種選擇
- mvn install到本地倉庫
- mvn deploy到私服
- 指定jar包路徑,從本地加載,例如如下pom形式
<dependency> <groupId>com.tievd.third</groupId> <artifactId>arcvideo</artifactId> <version>1.0</version> <scope>system</scope> <systemPath>${basedir}/lib/face-api-1.0.jar</systemPath> </dependency>
前文已經說到scope為system的依賴不會被打入最終的jar包,得通過配置插件等方式將依賴打入最終的jar包,所以這種方式一般很少使用。
還沒寫完,后續更精彩
本文為小編原創文章,首發于Java識堂,一個高原創,高收藏,有干貨的微信公眾號,一起成長,一起進步,歡迎關注