用 Spring Boot 實現電商系統 Web API (二)建立多模塊項目

大型項目,須要將代碼按不一樣功能,分紅不一樣模塊,這樣比較好管理和閱讀代碼,也有助於多人協做。java

1、項目結構

1.1 模塊說明

項目分紅5個模塊,分別以下:git

模塊名稱 說明
webapi HTTP接口層,對外提供 restful api
service 服務層
repo 數據訪問層
common 公用層
util 工具類層

 

1.2 建立模塊

模塊分層在實質是以文件目錄的形式實現的,新建一個名爲「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

image

這樣,一個多模塊的 Gradle 項目就建成了。緩存

 

1.3 檢查模塊

在「commerce」目錄中執行以下命令restful

gradle projects

若是顯示內容以下,說明我gradle已經認出咱們建立的項目了。app

image

 

2、添加代碼

2.1建立代碼目錄

在每一個子模塊中,添加用於存放Java代碼的目錄,目錄統一爲eclipse

src
  +-main
      +-java

image

 

2.2 包名規則

對於Java包名,咱們統一規範爲:頂級包名.項目名.模塊名,頂級包名能夠隨意取,好比:hang.commerce.webapi。

 

2.3 各個模塊代碼

爲了更清晰說明各個模塊是如何組織起來的,咱們在每一個模塊都加入代碼,而後讓他們互相引用。

咱們假設要實現這樣一個API:經過GET請求接口,接口返回一個用戶的信息,用戶信息中包含用戶名和最後的登陸時間。

2.3.1 根目錄

在根目錄的 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'
    }
}

這裏主要是對各個子項目(模塊)進行通用配置,避免在每一個模塊中重複配置。

 

2.3.2 commerce-repo 模塊

添加 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"
}

 

2.3.3 commerce-util 模塊

咱們須要格式化用戶最後的登陸時間,在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 文件不須要增長任何內容。

 

2.3.4 commerce-service 模塊

服務模塊從 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"
}

 

2.3.5 commerce-common 模塊

爲了規範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 文件不須要增長任何內容。

2.3.6 commerce-webapi 模塊

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')
}

至此,整個項目的代碼和配置已經所有完成。

3、編譯運行

3.1 編譯

在 commerce 目錄中執行

gradle build

便可進行編譯。

3.2 運行

在 commerce 目錄中執行

gradle bootRun

便可運行整個項目。

4、gradle wrapper

若是別人的電腦上沒裝有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/

 

5、總結

一、在根目錄中加入 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')
}

 

6、附錄

源碼 https://github.com/jinghang/commerce/tree/master/0x02/commerce

相關文章
相關標籤/搜索