簡化基於Maven的Scala項目創建詳解

使用 Maven 來構建 Scala 項目方便雖然方便,但依賴於每一個開發人員都去配置一遍項目的必要依賴,包括 Scala 插件的編譯配置以及 Scala 的依賴類庫,讓每個人通過拷貝(Copy)之前的項目配置當然可以,但難免會出些紕漏。

而從頭開始所有相關配置項都配置一遍,又略顯繁瑣,那麼,我們可不可以想辦法來進一步簡化類似的基於 Maven 的 Scala 項目創建和配置?

使用代碼片段管理工具

實際上,我們通常都會有收集的嗜好,尤其是作爲一名軟件開發者,都會有一套自己的代碼片段管理工具或者套路,將一些常用的或者自己感覺經典的代碼片段及配置內容進行摘錄並保存。

現在有很多不錯的代碼片段管理工具,比如 Mac 系統上的 Dash(

https://kapeli.com/dash),它可以幫助我們一站式索引技術文檔並管理代碼片段,有了像 Dash 這樣的工具,我們就可以將基於 Maven 的 Scala 項目配置文件整理成一個代碼片段(Snippet)進行管理。

然後在使用的時候直接在 Maven 的 pom.xml 中輸入該代碼片段的標誌信息,mvn.scala.cfg`Dash 將會自動將我們配置的代碼片段的內容展開並替換現有 pom.xml 的內容。

這種方法簡單、快速、模板化、可重複,但只適合單一開發人員,如果一個團隊或者組織內部所有人都使用這種方式,代碼片段的內容同步,以及 Dash 等軟件的授權等都是隨之衍生出來需要進一步解決的問題。所以,爲了能夠使更大層面的開發者收益,我們需要尋找更加有效的方法。




創建 spring-boot-starter-scala 簡化 Scala 依賴配置

對於基於 Maven 的 Scala 項目來說,以下依賴是必不可少的:

<dependency>    <groupId>org.scala-lang</groupId>    <artifactId>scala-library</artifactId>    <version>${scala.version}</version></dependency><dependency>    <groupId>org.scala-lang</groupId>    <artifactId>scala-compiler</artifactId>    <version>${scala.version}</version></dependency>

那麼,我們可以先從這裏進行簡化,爲了避免所有人都重複配置這部分依賴內容,可以構建一個 spring-boot-starter-scala 自動配置模塊,這樣,所有開發者只要在自己的項目中依賴這一個 spring-boot-starter-scala 自動配置模塊就可以了。


我們依然可以通過 
http://start.spring.io 腳手架服務創建 spring-boot-starter-scala 項目,然後將最終的 pom.xml 配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.keevol.springboot</groupId>    <artifactId>spring-boot-starter-scala</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <name>spring-boot-starter-scala</name>    <url>http://maven.apache.org</url>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <java.version>1.8</java.version>        <scala.version>2.11.7</scala.version>        <scala.maven.version>3.2.2</scala.maven.version>    </properties>    <build>        <plugins>            <plugin>                <groupId>net.alchim31.maven</groupId>                <artifactId>scala-maven-plugin</artifactId>                <version>${scala.maven.version}</version>                <executions>                    <execution>                        <id>compile-scala</id>                        <phase>compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>compile</goal>                        </goals>                    </execution>                    <execution>                        <id>test-compile-scala</id>                        <phase>test-compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>testCompile</goal>                        </goals>                    </execution>                </executions>                <configuration>                    <recompileMode>incremental</recompileMode>                    <scalaVersion>${scala.version}</scalaVersion>                    <args>                        <arg>-deprecation</arg>                    </args>                    <jvmArgs>                        <jvmArg>-Xms64m</jvmArg>                        <jvmArg>-Xmx1024m</jvmArg>                    </jvmArgs>                </configuration>            </plugin>        </plugins>    </build>    <dependencies>        <dependency>            <groupId>org.scala-lang</groupId>            <artifactId>scala-library</artifactId>            <version>${scala.version}</version>        </dependency>        <dependency>            <groupId>org.scala-lang</groupId>            <artifactId>scala-compiler</artifactId>            <version>${scala.version}</version>        </dependency>    </dependencies></project>

然後通過 mvn install 或者 mvn deploy 將其發佈。


現在,創建任何一個基於 Maven 的 Scala 項目時,pom.xml 配置內容就可以簡化如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.keevol.springboot</groupId>    <artifactId>spring-boot-starter-scala-user</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <name>spring-boot-starter-scala-user</name>    <url>http://maven.apache.org</url>    <dependencies>        <dependency>            <groupId>com.keevol.springboot</groupId>            <artifactId>spring-boot-starter-scala</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>    </dependencies></project>

是不是更加簡潔和方便了?但是,等一下,如果大家都這樣認爲,那就有問題了。


實際上,這樣的做法是行不通的,Maven 項目的依賴可以傳遞依賴(Transitive dependency),但是插件(Plugin)卻不行。如果我們真得在前面的項目配置上進行工作的話,spring-boot-starter-scala-user 可以編譯成功,可以打包成功,可以發佈成功,但是,在整個過程中 Maven 無法識別 Scala 代碼。

所以,如果要讓基於 spring-boot-starter-scala 的想法可以實現,我們需要將 scala-maven-plugin 從 spring-boot-starter-scala 項目中移到 spring-boot-starter-scala-user 項目。

spring-boot-starter-scala 項目的配置現如下:




<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.keevol.springboot</groupId>    <artifactId>spring-boot-starter-scala-user</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <name>spring-boot-starter-scala-user</name>    <url>http://maven.apache.org</url>    <dependencies>        <dependency>            <groupId>com.keevol.springboot</groupId>            <artifactId>spring-boot-starter-scala</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>    </dependencies></project>

spring-boot-starter-scala-user 的項目配置則如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.keevol.springboot</groupId>    <artifactId>spring-boot-starter-scala-user</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <name>spring-boot-starter-scala-user</name>    <url>http://maven.apache.org</url>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <java.version>1.8</java.version>        <scala.version>2.11.7</scala.version>        <scala.maven.version>3.2.2</scala.maven.version>    </properties>    <build>        <plugins>            <plugin>                <groupId>net.alchim31.maven</groupId>                <artifactId>scala-maven-plugin</artifactId>                <version>${scala.maven.version}</version>                <executions>                    <execution>                        <id>compile-scala</id>                        <phase>compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>compile</goal>                        </goals>                    </execution>                    <execution>                        <id>test-compile-scala</id>                        <phase>test-compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>testCompile</goal>                        </goals>                    </execution>                </executions>                <configuration>                    <recompileMode>incremental</recompileMode>                    <scalaVersion>${scala.version}</scalaVersion>                    <args>                        <arg>-deprecation</arg>                    </args>                    <jvmArgs>                        <jvmArg>-Xms64m</jvmArg>                        <jvmArg>-Xmx1024m</jvmArg>                    </jvmArgs>                </configuration>            </plugin>        </plugins>    </build>    <dependencies>        <dependency>            <groupId>com.keevol.springboot</groupId>            <artifactId>spring-boot-starter-scala</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>    </dependencies></project>

現在看來,spring-boot-starter-scala 只是幫助我們簡化了依賴管理,但對於 Scala 編譯插件相關的簡化則於事無補,像 spring-boot-starter-scala-user 這樣的一大批基於 Maven 的 Scala 項目還是要一遍一遍地配置 Scala 編譯插件相關內容。


不過,如果我們暫時能夠忍受每個項目自己配置 Scala 編譯插件的話(個人、小團隊一般可以甚至也願意),可以先進一步簡化依賴 spring-boot-starter-scala 項目的 
SpringBoot 微服務項目的創建。

我們經常用的 

http://start.spring.io 它羅列了很多現有的 spring-boot-starter 自動配置模塊供我們在創建 SpringBoot 項目的時候使用,如圖 1 所示。


圖片
圖 1  SPRING INITIALIZR自動配置模塊可用列表示意圖


如果將我們的 spring-boot-starter-scala 也加入進去,大家在創建項目的時候就可以不用配置 pom.xml 嗎?

部署在 

http://start.spring.io 的 SPRING INITIALIZR 項目服務實例我們是無權變更的,不過,我們可以在自己的公司或者組織內部搭建一個私有的 SPRING INITIALIZR 服務實例。

教程前面也跟大家提到過,只要到 

https://github.com/spring-io/initializr 獲取項目然後部署到自己的服務,然後根據 https://github.com/spring-io/initializr#running-your-own-instance 的說明啓動服務即可。

要將我們的 spring-boot-starter-scala 加入 SPRING INITIALIZR 的自動配置模塊選擇列表,需要先對自有的 SPRING INITIALIZR 實例進行一定的配置。

使用任何自己喜歡的文本編輯器打開 application.yml 配置文件(位於 {SPRING INITIALIZR 根目錄}/initializr-service/),然後在 dependencies 部分的末尾添加如下配置內容:






dependencies:- name: Core content:- name: Security id: securitydescription: Secure your application viaspring-security weight: 100-name: AOP id: aop description: Aspect-oriented programming includingspring-aop and AspectJ ...- name: Ops content:- name: Actuator id:actuator description: Production ready features tohelp you monitor andmanage your application ...- name: Scala content:- name: Scala id: scaladescription: API documentation for the Actuatorendpoints repository:local-repo groupId: com.wacai.springbootartifactId:spring-boot-starter-scala version: 0.0.1-SNAPSHOT

我們添加了一個名爲 Scala 的展示配置區塊,然後將 spring-boot-starter-scala 作爲配置內容配置其中。

但這還不夠,我們還需要告訴 SPRING INITIALIZR,當用戶選擇了 spring-boot-starter-scala 之後,在生成項目時,需要到哪裏獲取這個依賴,既然我們明確指定了 repository:local-repo,那麼,就要配置一下這個名字爲 local-repo 的倉庫,讓 SPRING INITIALIZR 知道這個 local-repo。

在 application.yml 配置文件中的 initializr->env 部分添加一個 repositories 相關配置如下:





initializr: env: boms: vaadin-bom: groupId: com.vaadin artifactId: vaadin-bom
version: 7.5.5 cloud-bom: groupId: org.springframework.cloud ...
repositories: local-repo: name: local-maven-repo
url: file:///Users/fujohnwang/.m2 snapshotsEnabled: true 


我們添加了 local-repo 指向筆者工作機本地的 maven 倉庫地址,大家也可以將自己公司或者組織內部私有 maven 倉庫也添加進來。

實際上,在配置 dependenies 的時候,並不一定要配置 repository:local-repo,這裏純粹是爲了讓大家瞭解這部分配置的更多信息。

關於 SPRING INITIALIZR 的更多配置詳細說明,可以參考 



https://github.com/spring-io/initializr/wiki/Configuration-format

以上所有配置完備後,直接在 {SPRING INITIALIZR根目錄}/initializr-service/ 下執行 spring run app.groovy 並最終啓動 SPRING INITIALIZR,現在,訪問 SPRING INITIALIZR 的服務地址就可以看到我們的最終成果了。

只要選擇並單擊生成項目之後,最終獲得的項目配置中就已經自動獲得了 spring-boot-starter-scala 的配置,配置代碼如下所示。



<dependency>    <groupId>com.wacai.springboot</groupId>    <artifactId>spring-boot-starter-scala</artifactId>    <version>0.0.1-SNAPSHOT</version></dependency>

關於基於 Maven 的 Scala 項目的依賴治理以及 spring-boot-starter-scala,我們暫時就介紹到這裏。雖然我們在依賴治理和工具的覆蓋度、易用度層面做出了很好的探索,但依然沒有簡化 Scala 編譯插件配置的繁瑣操作,大家還需要繼續努力。

創建公司和組織級別的 Scala 項目的 parent pom

目前爲止,Scala 相關的依賴管理已經比較令人滿意了,現在,我們只要主攻如何簡化 Scala 編譯插件配置的簡化就可以了。

好消息就是,Maven 的插件(Plugins)雖然不能通過組合的方式重用,但可以通過繼承的方式搞定,所以,我們可以構建一個公司或者組織級別 Maven 項目的 parent pom 專門用於基於 Maven 的 Scala 項目。

下面是我們的示例項目,只有一個 pom.xml 定義:



<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.3.2.RELEASE</version>        <relativePath />    </parent>    <groupId>com.keevol.maven</groupId>    <artifactId>scala-parent</artifactId>    <version>0.0.1-SNAPSHOT</version>    <packaging>pom</packaging>    <name>scala-parent</name>    <url>http://maven.apache.org</url>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <java.version>1.8</java.version>        <scala.version>2.11.7</scala.version>        <scala.maven.version>3.2.2</scala.maven.version>    </properties>    <build>        <plugins>            <plugin>                <groupId>net.alchim31.maven</groupId>                <artifactId>scala-maven-plugin</artifactId>                <version>${scala.maven.version}</version>                <executions>                    <execution>                        <id>compile-scala</id>                        <phase>compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>compile</goal>                        </goals>                    </execution>                    <execution>                        <id>test-compile-scala</id>                        <phase>test-compile</phase>                        <goals>                            <goal>add-source</goal>                            <goal>testCompile</goal>                        </goals>                    </execution>                </executions>                <configuration>                    <recompileMode>incremental</recompileMode>                    <scalaVersion>${scala.version}</scalaVersion>                    <args>                        <arg>-deprecation</arg>                    </args>                    <jvmArgs>                        <jvmArg>-Xms64m</jvmArg>                        <jvmArg>-Xmx1024m</jvmArg>                    </jvmArgs>                </configuration>            </plugin>        </plugins>    </build>    <dependencies>        <dependency>            <groupId>org.scala-lang</groupId>            <artifactId>scala-library</artifactId>            <version>${scala.version}</version>        </dependency>        <dependency>            <groupId>org.scala-lang</groupId>            <artifactId>scala-compiler</artifactId>            <version>${scala.version}</version>        </dependency>    </dependencies></project>

這個用作父項目(parent)的 maven 項目,其 pom.xml 的定義中將 Scala 編譯插件(scala-maven-plugin)的配置以及 Scala 相關類庫的依賴配置都囊括在內,這樣,所有繼承了這個父項目的子項目就都可以直接享受這些配置,而不用自己再去一一進行配置了,比如:

<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.keevol.maven</groupId>        <artifactId>scala-parent</artifactId>        <version>0.0.1-SNAPSHOT</version>    </parent>    <groupId>com.keevol.maven</groupId>    <artifactId>scala-parent-user</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <name>scala-parent-user</name>    <url>http://maven.apache.org</url>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    </properties>    <dependencies>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>3.8.1</version>            <scope>test</scope>        </dependency>    </dependencies></project>

父項目的 packaging 類型爲 pom,而不是一般的 jar 或者 war 等。

使用 Maven Archetype 簡化項目創建

不管是使用什麼語言開發,創建項目都是一個永恆的主題,作爲一線的開發工程師和領導者們往往也會爲團隊內部創建項目的千差萬別而頭疼,所以,腳手架(Scaffording)工具就成了研發體系中必要的一員。

如果我們緊盯和繼續挖掘 Maven 的潛力,除了使用前面提到的那些方法,Maven 的 Archetype 功能也是一種可以幫助我們簡化 Scala 項目創建的方案。

我們可以遵循 Maven 的 Archetype 規範創建一個 Scala 的模板項目(其實就是一個腳手架項目),然後將所有 Scala 項目相關的配置以及代碼等都放到這個模板項目中,當每個開發者希望創建一個 Scala 項目時,只要使用這個 Scala 的 Archetype 項目創建一個模板項目出來就可以直接開發了。

創建 Maven Archetype 項目有兩種方式:





  • 根據 Maven Archetype 項目規範從零開始創建項目。

  • 在一個現有的項目的基礎上創建 Maven Archetype 項目。


鑑於我們已經有了好幾個可以作爲規範或者“模範”使用的 Maven 的 Scala 項目,從零開始創建一個 Archetype 就沒有必要了,我們選擇第二種方式,即在現有項目的基礎上創建我們的 Maven Archetype 項目。

我們選擇 currency-webapi-with-scala 項目作爲模板項目用來創建 Archetype,然後再在生成的 Archetype 項目的基礎上進行裁剪,最終獲得一個理想的 Archetype。

首先,到 currency-webapi-with-scala 項目下執行 mvn archetype:create-from-project 命令,這將在項目的 target/generated-sources/archetype/ 目錄下生成一個“草稿”版的 Archetype 項目,我們將在這個“草稿”版的 Archetype 項目基礎上進行一定的裁剪。

我們可以對項目根目錄下的 pom.xml 中的必要信息進行修改,比如 groupId、artifactId、developers 等,這些都是將來我們引用這個 Archeteype 項目創建項目時所關心的信息。但使用 Archetype 項目創建出來的 Maven 項目會有哪些內容,則是由 Archetype 項目 src 下的內容決定的。

src/main/resources/archetype-resources 目錄下的內容大體上認爲就是生成目標項目的時候,生成的目標項目會擁有的東西,沒做裁剪之前,這些內容都是根據 currency-webapi-with-scala 的內容“拷貝(Copy)”過來的,我們會對這個符合 Maven 項目結構目錄下的內容進行裁剪。

比如刪除 currency-webapi.iml 等 IDE 相關元信息文件,添加 .gitignore 等必要元信息文件等。src/main/resources/META-INF/maven/archetype-metadata.xml 文件是進一步細化裁剪規則的配置文件。

以上裁剪完成後,我們執行如下命令完成最終的 Archetype 項目的發佈:

$ cd target/generated-sources/archetype/ $ mvn install 或者 mvn deploy 現在,要創建一個基於 Maven 的 Scala 項目,我們只要執行如下命令即可:















mvn archetype:generate -DarchetypeGroupId=com.mengma.springboot-DarchetypeArtifactId=currency-webapi-with-scala-archetype-DarchetypeVersion=0.0.1-SNAPSHOT-DgroupId=com.keevol.springboot-DartifactId=new-scala-project-name-Dversion=0.0.1-SNAPSHOT

以上命令執行完成後,在當前目錄下會創建一個 new-scala-project-name 的目錄,這就是一個完整的符合我們之前裁剪設定的基於 Maven 的 Scala 項目。

現在,團隊或者公司內任何一個開發者想要創建一個基於 Maven 的 Scala 項目,只要執行以上類似的一條 mvn archetype:generate 命令就可以了。




圖片


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章