• 用 Spring Boot 实现电商系统 Web API (二)创建多模块项目


    大型项目,需要将代码按不同功能,分成不同模块,这样比较好管理和阅读代码,也有助于多人协作。

    一、项目结构

    1.1 模块说明

    项目分成5个模块,分别如下:

    模块名称 说明
    webapi HTTP接口层,对外提供 restful api
    service 服务层
    repo 数据访问层
    common 公用层
    util 工具类层

    1.2 创建模块

    模块分层在实质是以文件目录的形式实现的,新建一个名为“commerce”的目录,然后在里边创建五跟文件夹,名字为“commerce-webapi”“commerce-service”、“commerce-repo”、“commerce-common”、“commerce-util”。在每个模块名前加“commerce-”前缀,是为了更好的和他模块区分,还有个一个是,每个模块都会单独生成一个jar包,加前缀也是为了避免和外部引用的jar包重名。

    创建好上述的五个文件夹后,再在“commerce”目录新建两个名为 “settings.gradle”和“build.gradle”的纯文本文件,在每个子目录中都建一个名为“build.gradle”的纯文本文件。

    在“setting.gradle”加入如下内容:

    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”文件都留空白,稍后我们再添加内容。最后,建立的文件和目录如下:

    image

    这样,一个多模块的 Gradle 项目就建成了。

    1.3 检查模块

    在“commerce”目录中执行如下命令

    gradle projects

    如果显示内容如下,说明我gradle已经认出我们创建的项目了。

    image

     

    二、添加代码

    2.1创建代码目录

    在每个子模块中,添加用于存放Java代码的目录,目录统一为

    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.1 编译

    在 commerce 目录中执行

    gradle build

    即可进行编译。

    3.2 运行

    在 commerce 目录中执行

    gradle bootRun

    即可运行整个项目。

    四、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/

    五、总结

    1、在根目录中加入 settings.gradle 的文本文件,文件内容写明子模块(子文件夹名),比如

    include 'commerce-util'
    include 'commerce-common'
    include 'commerce-repo'
    include 'commerce-service'
    include 'commerce-webapi'

    2、根目录加入 build.gradle 的文本文件,作为项目和各个子模块的通用配置。

    3、每个子模块都加入  build.gradle 的文本文件,作为子模块的配置。

    4、各个模块间的引用通过在 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

  • 相关阅读:
    我的友情链接
    我的友情链接
    我的友情链接
    我的友情链接
    我的友情链接
    我的友情链接
    我的友情链接
    以太坊:通信协议对象 shh
    以太坊:Truffle 概述
    以太坊:快速入门 Truffle
  • 原文地址:https://www.cnblogs.com/teafree/p/7737773.html
Copyright © 2020-2023  润新知