Maven-實作篇 (2)

延續上一回的實作,現在我們將從桌面視窗程式開始!

Desktop

  1. 依照以下資訊,建立 Maven 專案 (使用 maven-archetype-quickstart)
    • groupId: com.appx
    • artifactId: user-service-desktop
    • package: com.appx.userservice.desktop
    • version: 1.0-SNAPSHOT
  2. 這次不實作unit test,所以把 src/test/java 底下 AppTest.java 刪除,同時也移除 POM.xml 裡面 junit 的 dependency
  3. 在 POM.xml 增加以下 plugin 設定,除了 compiler plugin,也使用了 maven-shade-plugin 用來把整個桌面視窗程式打包成單一 jar 檔(包含dependency),可以看到在 package 這個lifecycle build phase 綁定了 shade goal,並且必須指定 main 方法所在的class
    (maven lifecycle的概念非常重要,請參考之前的文章)
  4. 加入 core 專案作為 dependency
  5. 下載以下程式,並放至 src/main/java 底下這個package com.appx.userservice.desktop
    https://drive.google.com/open?id=0B0C215CJ0AjoX2tPLVpVZlJYQkk
  6. 執行 mvn package ,可以發現 target 資料夾底下多了兩個 jar,其中 user-service-desktop-1.0-SNAPSHOT.jar 包含了相關的 dependencies

  7. 可以使用 jar -tf 指令來比較兩個 jar 的內容

    jar -tf user-service-desktop-1.0-SNAPSHOT.jar
    jar -tf original-user-service-desktop-1.0-SNAPSHOT.jar

  8. 執行 jar ,確認一切都 ok,id可以輸入 1 ~ 3,會看到查詢結果

Maven-實作篇 (1)

接下來會帶大家動手實作幾個 Maven 專案,從練習過程中我們會更進一步了解到 Maven 所帶來各種方便的特性!

我們將會建立一個簡單的用戶查詢系統,由三個專案所組成,分別是 core, desktop, web,其中 desktop, web 專案會將core 當作 dependency,如下圖所示:

  • core 專案代表整個系統的核心功能所在
  • desktop 專案負責視窗程式的操作介面
  • web 專案負責網頁的操作介面

Continue reading →

Maven-10-核心概念 dependency management

對於開發者來說,Maven最強大的功能莫過於 dependency management了!

在之前文章 Maven-05-一個簡單的POM範例 我們看到POM裡定義了一組 junit 的dependency,所以在執行 mvn install 時,Maven 會先檢查本機的 repository 是否有 junit 相關的 artifact ,如果沒有就會從網路上下載,然後放到 ~/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar

這邊我們可以觀察到 Maven 自動下載的東西除了jar 檔,還包括了其他檔案像是包了原始碼的jar,還有一個很關鍵的 junit-3.8.1.pom,為什麼 Maven 還會下載 junit的POM呢?

大家可以試著想想,如果今天我們需要的不是junit 這麼單純的套件,我們需要在專案裡使用 Hibernate,從它的 POM 我們得知 Hibernate 本身也有許多 dependencies,難道我們必須在自己專案的 POM 把這些 Hibernate 的 dependency 也全部都寫出來嗎??

答案當然是:NO !!

我們只需要定義 Hibernate dependency,Maven 就會自動把它關聯的 dependency 也一併下載回來,這種機制稱為 “transitive dependencies”, 實際上的運作方式是 Maven 會去查看 dependency 的 POM,並確認是否有相關的 dependency

在junit 的 <dependency> 定義裡,有一個 <scope>test</scope>,這是另一個重要的概念 ,常用的 dependency scope 有下列幾種:

  • compile: 如果沒有定義<scope>, 預設值就是 compile,顧名思義指的是在編譯專案程式碼時需要這個 dependency
  • test: 指的是在這兩個 goal compiler:testCompile (編譯測試程式碼), surefire:test (執行unit test) 才會引用的dependency,正式產出的 jar (或其它格式) 並不會包含 scope 為 test 的 dependency
  • provided: 通當在 web 專案比較常看到這種 scope,指的是在編譯時有需要,但在正式產出war時並不會包含在一起,像是  jsp, servlet 這種 dependency,我們會在開發時用到相關的api,但實際提供這些 dependency 的是運行 web app 的 ap server

更多 dependency scope 可參考
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope

Maven-09-核心概念 repositories

我們之前在 Maven-04-建置專案 裡執行了 mvn install 指令,如果是初次執行的話,會發現 maven 會自動從遠端 repository 下載很多 plugin 相關的檔案,這些檔案會存放在本機個人目錄下 .m2/repository 資料夾裡。現在我們一起來了解有關 repository 更多資訊吧!

Maven 官方預設的遠端 repository 在 http://repo1.maven.org/maven2/ ,如果有需要的話,像是公司內部私有專案的需求, Maven 也支援自行架設的 repository,其中比較有名的像是:

只要修改 maven 安裝目錄下的 conf/settings.xml 就可以指向自行架設的 repository

Maven repository 儲存了大大小小專案的成品 (project artifact),其中的目錄結構完全符合我們先前介紹過的 Maven coordinates,大家可以試著從官方的 http://repo1.maven.org/maven2/  來觀察,這邊以 hibernate-core  版本 5.1.0.Final 為例子來說明

  • hibernate-core  版本 5.1.0.Final 的 coordinates 如下,完整 POM 可參考這裡

    (packaging 預設 為 jar)
  • jar 檔的完整路徑如下
    http://repo1.maven.org/maven2/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar
  • 路徑的組成,都遵照一定的格式
    repository_location/groupId/artifactId/version/artifactIdversion.packaging

Maven 從遠端下載的 project artifacts 會存在放本機 local repository 裡 (預設是 個人目錄 .m2/repository,也可以透過 settings.xml 修改),之後當有需要在專案裡引用這些 artifacts 時,Maven 就會直接參考 local repository 而不再從網路上尋找,順帶一提,local repository 裡面的資料夾結構,也和遠端 repository 一樣

mvn install 執行之後產生的project artifact,最後就會放置在  local repository 裡,這麼做的目的是日後可以供其它專案來參照引用。在下一篇,我們將為大家介紹對開發人員來說 Maven 最最最重要的功能 — dependency 管理

[Java101 基礎篇] – Java 8 Lambda 的崛起

雖然Java 8 已經出來好一陣子了~  (記得是 2014的三月)
但還是有不少人會詢問什麼是 Lambda,什麼是 匿名類別 Anonymous class
藉著最近專案的機會,分享一下我們對 Lambda的了解,希望能有所幫助。

預計這一系列的文章,會有

  • Java 8 Lambda 的崛起
  • Java 8 Lambda 的語法
  • Java 8 Lambda 跟集合物件的應用
  • Java 8 Lambda 跟File I/O的應用

 

今天就先針對 Java 8 Lambda 的出現,先做個應用的說明 ~

讓我們從一個很常用的例子開始~  「使用者在畫面上按下按鈕,然後秀出 HelloWorld」
(*這部份機制,可應用在 Java Swing/FX  以及 Android )

 

以Java Swing 視窗程式為例,在 Lambda出現之前,我們可能的做法有下列幾項 ~

  1. 寫一個完全獨立的新類別  來實作 ActionListener,然後在JButton呼叫addActionListener時,當成參數傳入。

     
  2. 寫一個在main主程式 類別的內部類別(Inner class),然後在也是一樣,在JButton呼叫addActionListener時,當成參數傳入。

     
  3. 使用匿名類別(Anonymous Inner class),在JButton物件呼叫addActionListener 時,當成參數傳入

 

 

在Java 8 Lambda 出現後,我們可以用下列的寫法

jb.addActionListener( (a)-> System.out.println(“Hello”));
沒錯,就只要寫 一行,就可以有同樣的效果~~~

剩下的,就讓Java 8 Compiler 將程式碼 編譯成跟之前 actionListener 一樣的class code !!!

是不是很方便~~~ 趕快試試看囉~~

 

對了~

匿名類別 (Anonymous class)還不是很清楚?! 可以參考我們提供的教育訓練

 

 

參考資料:
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

http://www.coreservlets.com/java-8-tutorial/#setup

Maven-08-核心概念 coordinates

在 Maven-05-一個簡單的POM範例 我們稍微給大家介紹了最基本的 POM 內容,也提到了 POM 一開始的 groupId, artifactId, version  這三個資訊可以構成一個該專案特有的識別名稱

pom

這回就針對這幾項做更進一步的說明

  • groupId: 團隊名稱,通常的命令慣例是把網址倒過來寫的表示方式,例如 com.appx, org.apache 等等
  • artifactId: 專案名稱
  • version: 版本號,如果是還在開發中的專案,通常會以 SNAPSHOT 結尾

以上三個資訊可以做為一個專案的唯一識別名稱,不會有兩個專案具有同樣的groupId:artifactId:version

packaging (打包方式) 不是唯一識別名稱的一部份,不過也是 maven coordinates 的組成之一,這幾個資訊就構成了我們在各個公私有的 maven repositories 茫茫大海中,找尋到特定專案的唯一識別名稱

除了 POM 一開始宣告了該專案的 coordinates,如果想為專案增加其它 dependencies ,也是透過  coordinates 來指向其它專案,如上圖 <dependency>  內容所示,甚至在使用 plugin 時,同樣也是透過 coordinates 來指明特定的 plugin,如下所示

plugins

Maven-07-核心概念 lifecycle

還記得我們在 Maven-04-建置專案 裡所執行的 mvn install 嗎?

這個指令讓我們完成了專案的編譯、測試、打包、安裝(到repository)等步驟,而在上一篇 Maven-06-核心概念 plugins and goals 我們知道這些任務都是由各種 plugins 來負責的。那為什麼這樣一個簡短的指令可以一次完成這麼多任務呢?

我們必須要先了解所謂的 maven lifecycle,區分為以下三個種類:

  • clean: 把建置好的專案相關產出檔案給清除掉 (刪除 target 資料夾)
  • site: 產生專案網站,如下圖範例

    Screen Shot 2017-03-14 at 2.52.21 PM
  • default:  內建的lifecycle,專案建置的流程都是在這裡定義的

maven lifecycle 裡還定義了多個 build phase,我們之前所執行的 mvn install,就是執行了 default lifecycle 裡 install 這個 build phase,有關 lifecycle build phases 的詳細定義,可以參考 https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference

從以上的 lifecycle 官方文件,我們可以看到 default lifecycle 有20幾個 build phase,而 install 是倒數第二個,這邊有一點要特別注意的是,雖然我們下的指令是 mvn install,但 maven 會從第一個 build phase (validate) 一路按順序執行到 install,這個現象其實也很容易理解,畢竟沒有之前 build phase 的產物的話,maven 也不知道應該 install 什麼東西

回到最一開始的問題:為什麼 mvn install 一個指令可以完成這麼多任務呢?原因在於  plugin goals 可以指定這個 goal 會在哪個 lifecycle build phase 裡執行 (如果沒有特別指定的話,也會有預設值)。所在在執行 mvn install 時,maven 會去找找有哪些 plugin goals 指定了  build phase,然後就依照 lifecycle 的順序執行下去。lifecycle 與 plugin goals 的對應關係,可參考 https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Built-in_Lifecycle_Bindings

我們可以非常靈活地使用 plugin goals 來完成各種任務,但不同的專案可能會有不同的plugin goals 使用方式,只有透過 lifecycle 這麼一套 maven 制定的標準流程,之後在轉換到開發其他 maven 專案時,我們還是可以使用一致的指令來建置專案,無形之中為開發人員減輕了不少負擔!

Maven-06-核心概念 plugins and goals

這一篇要給大家講講 Maven 之所以能夠完成各種任務的關鍵元件:plugins  and  goals

我們在 Maven-03-建立第一個 Maven專案 裡一開始使用了一道指令來建立專案,

01

  • 其中的 archetype 就是 plugin 名稱, archetype plugin 讓我們可以依照既有的專案樣版來產生專案,或是由既有專案產生出樣版,以方便之後重複使用
  • generate 代表 goal 名稱,四個參數 groupId, artifactId, package, version 是要傳給 generate 這個 goal 的參數。

一個 Maven plugin 其實就是多組 goal 所集合而成的,goal 所代表的是一個任務,並且允許我們在執行時輸入參數,下列出幾個常用的 plugin

更多的 plugins 可參考 https://maven.apache.org/plugins/maven-jar-plugin/

在了解了 plugin and goals 的定義之後,我們或許可以從另一個角度來解釋 maven:maven 本身是一個具有基本功能的核心平台,它只知道如何解析mvn指令、解析POM、下載所需的plugins並依照設定執行,所以像是對程式碼進行編繹、或打包專案檔,這些事情就會交由plugins 來完成。

我們已經知道了 maven plugins 可以完成開發專案時的各項任務,從以上範例大致也可以看出要產生一個jar檔需經過哪些步驟,但是如果每次都要依照這些步驟一個一個執行,恐怕會影響開發人員的工作效率。因此,下一篇將給大家介紹 maven 另一個核心概念 “lifecycle”,讓我們透過簡短的指令操作,就可以完成一連串相關的任務!

Maven-05-一個簡單的POM範例

我們先來看看Maven自動產生的pom.xml

Screen Shot 2017-03-07 at 9.34.58 PM

  • pom.xml 包含了整個 maven專案許多資訊,一般狀況下專案裡的pom.xml 遠比上面這個範例複雜的多,還會包含更多的dependency或是自定義的plugin等等。
  • 一開始的groupId, artifactId, version, packaging 通稱為maven coordinates (座標),前三個資訊可以構成一個該專案特有的識別名稱
  • name, url 則是定義了專案名稱、專案網站資訊。這邊的name是讓開發人員自己看的。通常我們使用各種支援maven的開發工具時(Eclipse, NetBeans, IntelliJ IDEA) ,這些開發工具會把artifactId當作專案名稱
  • properties 定義了 pom.xml 裡面的「全域屬性」,如上所示,定義了一個 “project.build.sourceEncoding = UTF-8″的property,如果在pom.xml其它地方需要參照”UTF-8″這個設定,我們可以直接使用 ${project.build.sourceEncoding} 這樣的表示方式
  • 一組dependency 包含了 groupId, artifactId, version 以及
    • packaging 可忽略不寫,默認為jar
    • scope 如果不寫,默認為compile,這邊的junit scope 為test,意思是說這個dependency只有在跑unit test時才會使用到,之後專案要打包釋出時,並不會包含scope為test的dependency,有關dependency scope ,會在之後的文章中詳細說明!

maven專案可以具有上下從屬關係,pom.xml 會繼承每一階層的設定。而單一階層的專案也會繼承自maven最上層的 super-POM (如同Java裡所有物件都繼承自Object),大家可以試著在專案根目錄執行 mvn help:effective-pom,看看「完整版」的pom會長什麼樣子!

咱們下回見囉~

Maven-04-建置專案

建置 Maven專案

  1.  只需要一個簡單的 mvn install 指令,我們便可以建置maven專案。初次執行時會耗費一些時間,因為maven會自動下載所需的plugin並存放於 ~/.m2/repository底下,之後再次執行就只會出現如下簡單的執行歷程
    01
     

    建置的程過也包含了執行unit test!

    02

  2. That’s All!!  我們已經完成了編譯、測試、打包、安裝(到repository)一個最基本的maven專案。為了確定打包出來的jar檔沒有問題,可以執行以下測試,看到Hello World!就成功了!
    03