大型項目,須要將代碼按不一樣功能,分紅不一樣模塊,這樣比較好管理和閱讀代碼,也有助於多人協做。java
項目分紅5個模塊,分別以下:git
模塊名稱 | 說明 |
webapi | HTTP接口層,對外提供 restful api |
service | 服務層 |
repo | 數據訪問層 |
common | 公用層 |
util | 工具類層 |
模塊分層在實質是以文件目錄的形式實現的,新建一個名爲「commerce」的目錄,而後在裏邊建立五跟文件夾,名字爲「commerce-webapi」「commerce-service」、「commerce-repo」、「commerce-common」、「commerce-util」。在每一個模塊名前加「commerce-」前綴,是爲了更好的和他模塊區分,還有個一個是,每一個模塊都會單獨生成一個jar包,加前綴也是爲了不和外部引用的jar包重名。github
建立好上述的五個文件夾後,再在「commerce」目錄新建兩個名爲 「settings.gradle」和「build.gradle」的純文本文件,在每一個子目錄中都建一個名爲「build.gradle」的純文本文件。web
在「setting.gradle」加入以下內容:spring
rootProject.name='commerce' include 'commerce-util' include 'commerce-common' include 'commerce-repo' include 'commerce-service' include 'commerce-webapi'
第一行「rootProject.name='commerce'」是可選,不加入也可,setting.gradle文件內容主要是爲了說明項目中包含哪些模塊。全部的「build.gradle」文件都留空白,稍後咱們再添加內容。最後,創建的文件和目錄以下:api
這樣,一個多模塊的 Gradle 項目就建成了。緩存
在「commerce」目錄中執行以下命令restful
gradle projects
若是顯示內容以下,說明我gradle已經認出咱們建立的項目了。app
在每一個子模塊中,添加用於存放Java代碼的目錄,目錄統一爲eclipse
src +-main +-java
對於Java包名,咱們統一規範爲:頂級包名.項目名.模塊名,頂級包名能夠隨意取,好比:hang.commerce.webapi。
爲了更清晰說明各個模塊是如何組織起來的,咱們在每一個模塊都加入代碼,而後讓他們互相引用。
咱們假設要實現這樣一個API:經過GET請求接口,接口返回一個用戶的信息,用戶信息中包含用戶名和最後的登陸時間。
在根目錄的 build.gradle 文件加入以下內容:
subprojects{ apply plugin:'java' sourceCompatibility=1.8 group='hang.commerce' version='0.0.1' repositories{ jcenter(); mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies{ } sourceSets{ main{ resources{ srcDirs=['src/main/resources', 'src/main/java'] } } } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } }
這裏主要是對各個子項目(模塊)進行通用配置,避免在每一個模塊中重複配置。
添加 User.java做爲數據層的實體類
commerce/commerce-repo/src/main/java/hang/commerce/repo/User.java
package hang.commerce.repo; import org.springframework.stereotype.Component; /** * 用戶類 */ @Component public class User { /** * 名稱 */ private String name; /** * 最後登陸時間 */ private String lastLoginTime; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLastLoginTime() { return lastLoginTime; } public void setLastLoginTime(String lastLoginTime) { this.lastLoginTime = lastLoginTime; } }
在User類上,我使用了「@Component」註解,這樣就可使用Spring Boot的依賴注入(IOC)功能,在使用的地方用「@Autowired」註解來註明要注入的地方。
因爲要使用Component這個註解類,因此咱們還須要在 commerce-repo 模塊中的 build.gradle 加入以下內容以引入JAR包。
commerce/commerce-repo/build.gradle
dependencies { compile "org.springframework:spring-context:5.0.0.RELEASE" }
咱們須要格式化用戶最後的登陸時間,在util模塊增長一個時間工具類。
commerce/commerce-util/src/main/java/hang/commerce/util/DateTimeTool.java
package hang.commerce.util; import java.text.SimpleDateFormat; import java.util.Date; /** * 時間工具類 */ public class DateTimeTool { /** * 日期格式化 * @param time 日期 * @return yyyy-MM-dd HH:mm:ss 格式的日期 */ public static String format(Date time){ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time); } }
commerce-util 模塊的 build.gradle 文件不須要增長任何內容。
服務模塊從 repo模塊取數據,而後給webapi模塊提供數據。在本示例中,咱們只是簡單提供一個 get() 方法,返回User對象。爲了指明這個是一個服務,咱們在 UserService 類上加了 「@Service」 註解,這樣Spring會自動完成依賴注入,在須要 Service 的地方,經過 「@Autowired」 註解注入 ,好比,咱們須要User對象時,不是直接 new 出來,而是經過 Autowired 注入。代碼中,設置用戶的最後登陸時間時,用到了 「commerce-util」的 「DateTimeTool」工具類中的方法。
commerce/commerce-service/src/main/java/hang/commerce/service/UserService.java
package hang.commerce.service; import hang.commerce.repo.User; import hang.commerce.util.DateTimeTool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; @Service public class UserService { @Autowired private User user; public User get(){ user.setName("Hello"); user.setLastLoginTime(DateTimeTool.format(new Date())); return user; } }
在 service 層,咱們就使用到了 gradle 的模塊間引用功能。gradle 模塊間的引用只須要一行代碼就能夠實現,
commpile project(':模塊名')
模塊名稱前的「:」是分隔符,至關於Linux系統中的文件路徑中 「/」,第一個「:」表示根目錄。
因爲「commerce-service」模塊使用到了「commerce-repo」模塊和「commmerce-util」模塊中的代碼,因此,咱們須要在 「build.gradle」加入依賴。
對外部Jar包的依賴,寫法以下
compile "org.springframework:spring-context:5.0.0.RELEASE"
規則是 compile 「組織名稱:包名:版本號」,gradle 會自動根據 build.gradle 文件中的 repositories{} 的配置到網上下載Jar包,緩存到本地。
在 commerce-service 模塊中,buidl.gradle 的寫法以下:
commerce/commerce-service/build.gradle
dependencies { compile project(':commerce-repo') compile project(':commerce-util') compile "org.springframework:spring-context:5.0.0.RELEASE" }
爲了規範API的返回值格式,咱們在 commerce-common 模塊增長了API返回值類
commerce/commerce-common/src/main/java/hang/commerce/common/ApiResult.java
package hang.commerce.common; /** * API返回值對象 * @param <T> 返回的數據類型 */ public class ApiResult<T> { /** * 狀態碼 */ private ApiResultCode code; /** * 返回的消息字符串 */ private String message; /** * 返回的數據 */ private T data; /** * 異常名稱 */ private String exception; /** * 訪問路徑 */ private String path; public ApiResultCode getCode() { return code; } public void setCode(ApiResultCode code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getException() { return exception; } public void setException(String exception) { this.exception = exception; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } }
commerce/commerce-common/src/main/java/hang/commerce/common/ApiResultCode.java
package hang.commerce.common; /** * API 返回值狀態碼 */ public enum ApiResultCode { /** * 成功 */ OK, /** * 失敗 */ Failed, /** * 未找到資源 */ NotFound, /** * 未受權 */ NotAuth }
因爲 commerce-common 模塊沒有使用其餘模塊或者外部的Jar包,因此 commerce-common模塊的 build.gradle 文件不須要增長任何內容。
commerce-webapi 模塊是入口模塊,須要配置 spring boot。因爲咱們使用了Spring Boot的依賴注入功能,因此咱們須要在入口類上加上註解指明要IOC要掃描的包範圍,以下
@ComponentScan(value = "hang.commerce") // 依賴注入掃描
commerce/commerce-webapi/src/main/java/hang/commerce/webapi/WebApiApplication.java
package hang.commerce.webapi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; /** * spring boot 應用入口 */ @ComponentScan(value = "hang.commerce") // 依賴注入掃描 @SpringBootApplication public class WebApiApplication { public static void main(String[] args){ SpringApplication.run(WebApiApplication.class, args); } }
咱們將全部的API統一放到 controller 包中。
使用
@RestController
指明,這是 restfule api。
使用
@RequestMapping("api/home")
指明 API的前綴。
在 Controller中,須要使用到服務層的服務時,只須要寫以下代碼便可,Spring Boot IOC 會自動幫咱們處理餘下的事情。
@Autowired private UserService userService;
controller的代碼以下:
commerce/commerce-webapi/src/main/java/hang/commerce/webapi/controller/HomeController.java
package hang.commerce.webapi.controller; import hang.commerce.common.ApiResult; import hang.commerce.common.ApiResultCode; import hang.commerce.repo.User; import hang.commerce.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("api/home") @RestController public class HomeController { @Autowired private UserService userService; @GetMapping("/") public ApiResult<User> get(){ User user = userService.get(); ApiResult<User> result = new ApiResult<>(); result.setCode(ApiResultCode.OK); result.setMessage("獲取成功"); result.setData(user); return result; } }
在commerce-webapi模塊中的 build.gradle 和正常的Spring Boot程序配置無異,只是增長了對項目中其餘模塊的引用
compile project(':commerce-common') compile project(':commerce-util') compile project(':commerce-repo') compile project(':commerce-service')
最後,build.gradle 的配置以下
commerce/commerce-webapi/build.gradle
buildscript { ext { springBootVersion = '2.0.0.M5' } repositories { mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } group = 'hang.commerce' version = '0.0.1' sourceCompatibility = 1.8 apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' repositories { mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies{ compile project(':commerce-common') compile project(':commerce-util') compile project(':commerce-repo') compile project(':commerce-service') compile('org.springframework.boot:spring-boot-starter-web') }
至此,整個項目的代碼和配置已經所有完成。
在 commerce 目錄中執行
gradle build
便可進行編譯。
在 commerce 目錄中執行
gradle bootRun
便可運行整個項目。
若是別人的電腦上沒裝有gradle,那麼就沒法編譯和運行咱們的程序,爲了解決這個問題,咱們能夠把gradle附帶到代碼中。
在 commerce 目錄中執行
gradle wrapper
以後,gradle會在目錄中加入
commerce +--gradlew +--gradlew.bat +--gradle +--wrapper +--gradle-wrapper.jar +--gradle-wrapper.properties
這樣,當別人拿到咱們的代碼後,不須要安裝 gradle 程序,只須要把以前的命令「gradle」改爲 「gradlew」就能夠了,好比
gradlew build
當執行 「gradlew」命令時,會根據 gradle/wrapper/gradlw-wrapper.properties文件中的配置,到網上下載 gradle 程序,存放在 用戶目錄/.gradle/wrapper/dists/gradle-x.x.x-bin 目錄,好比
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
會到網上下載 gradle-4.2.1-bin.zip 存放到 用戶目錄/.gradle/wrapper/dists/gradle-4.2.1-bin/隨機字符串/gradle-4.2.1/
一、在根目錄中加入 settings.gradle 的文本文件,文件內容寫明子模塊(子文件夾名),好比
include 'commerce-util' include 'commerce-common' include 'commerce-repo' include 'commerce-service' include 'commerce-webapi'
二、根目錄加入 build.gradle 的文本文件,做爲項目和各個子模塊的通用配置。
三、每一個子模塊都加入 build.gradle 的文本文件,做爲子模塊的配置。
四、各個模塊間的引用經過在 dependencies{} 中加入 compile project(「:子模塊名」),好比:
dependencies{ compile project(':commerce-common') compile project(':commerce-util') compile project(':commerce-repo') compile project(':commerce-service') compile('org.springframework.boot:spring-boot-starter-web') }
源碼 https://github.com/jinghang/commerce/tree/master/0x02/commerce