最详细的 Spring Boot 多模块开发与排坑指南

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 最详细的 Spring Boot 多模块开发与排坑指南

来自:未读代码

创建项目

创建一个 SpringBoot 项目非常的简单,简单到这里根本不用再提。你可以在使用 IDEA 新建项目时直接选择 Spring Initlalize 创建一个 Spring Boot 项目,也可以使用 Spring 官方提供的 Spring Boot 项目生成页面得到一个项目。

下面介绍一下使用 Spring 官方生成的方式,如果你已经有了一个 Spring Boot 项目,这部分可以直接跳过

  • 打开 https://start.spring.io/ 
  • 填写 `group` 和 `Artifact` 信息,选择依赖(我选择了 Spring Web 和 Lombok )。
    最详细的 Spring Boot 多模块开发与排坑指南
    spring 官网创建初始项目
  • 点击 `Generate` 按钮下载项目。
  • 打开下载的项目,删除无用的 `.mvn` 文件夹,`mvnw` 、 `mvnw.cmd` 、`HELP.md` 文件。
  • 填写 group Artifact 信息,选择依赖(我选择了 Spring Web 和 Lombok )。

    打开下载的项目,删除无用的 .mvn 文件夹, mvnw mvnw.cmd HELP.md 文件。

    到这里已经得到了一个 Spring Boot 初始项目了,我们直接导入到 IDEA 中,看一眼 pom.xml 的内容。

    
    ?xml version="1.0" encoding="UTF-8"?
    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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        modelVersion4.0.0/modelVersion
        parent
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-parent/artifactId
            version2.2.5.RELEASE/version
            relativePath/ !-- lookup parent from repository --
        /parent
        groupIdcom.wdbyte/groupId
        artifactIdspringboot-module-demo/artifactId
        version0.0.1-SNAPSHOT/version
        namespringboot-module-demo/name
        descriptionDemo project for Spring Boot/description
    
        properties
            java.version1.8/java.version
        /properties
    
        dependencies
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-web/artifactId
            /dependency
    
            dependency
                groupIdorg.projectlombok/groupId
                artifactIdlombok/artifactId
                optionaltrue/optional
            /dependency
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-test/artifactId
                scopetest/scope
                exclusions
                    exclusion
                        groupIdorg.junit.vintage/groupId
                        artifactIdjunit-vintage-engine/artifactId
                    /exclusion
                /exclusions
            /dependency
        /dependencies
    
        build
            plugins
                plugin
                    groupIdorg.springframework.boot/groupId
                    artifactIdspring-boot-maven-plugin/artifactId
                /plugin
            /plugins
        /build
    /project
    

    把目录结构调整成自己想要的结构,然后添加 controller entity 用于测试。

    ProductController 类源代码。

    
    @RestController
    @RequestMapping("/product")
    public class ProductController {
    
        /**
         * 获取商品列表
         *
         * @return
         */
        @GetMapping("/list")
        public Map list() {
            // 模拟查询商品逻辑
            Product product = new Product();
            product.setProductName("小米粥");
            product.setProductPrice(new BigDecimal(2.0));
            product.setProductStock(100);
    
            MapString, Object resultMap = new HashMap();
            resultMap.put("code", 000);
            resultMap.put("message", "成功");
            resultMap.put("data", Arrays.asList(product));
            return resultMap;
        }
    }
    
    

    Product 类源代码。

    
    @Data
    public class Product {
        /** 商品名称. */
        private String productName;
        /** 商品价格. */
        private BigDecimal productPrice;
        /** 商品库存。*/
        private int productStock;
    }
    

    模块化

    借助 IDEA 工具可以快速的把项目改造成 maven 多模块,这里我们把准备测试 demo 拆分为 common 和 web 两个模块,common 模块存放实体类。web 模块存放 controller 层(这里项目虽小,拆分只是为了演示)。话不多说,直接开始。

  • 配置主 pom.xml **打包方式 为 pom**
    ?xml version="1.0" encoding="UTF-8"?
    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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        modelVersion4.0.0/modelVersion
        !-- 配置主 pom 打包方式为 pom --
        packagingpom/packaging
        ....
        ....
    
  • 创建 common 模块 项目直接 new - module。
    最详细的 Spring Boot 多模块开发与排坑指南
    创建模块
    选择 maven - next,填写模块名称。
    最详细的 Spring Boot 多模块开发与排坑指南
    填写模块名称
    继续 next 完成模块创建。
  • 创建 web 模块 web 模块的创建和 common 模块如出一辙,不再赘述。完成两个模块的创建之后,你会发现你的主 pom.xml 文件里自动添加了 module 部分。
    modules
        moduleproduct-common/module
        moduleproduct-web/module
    /modules
    
  • 移动代码到指定模块 移动 `Product.java` 到 `product-common` 模块,其他部分代码和 resource 部分直接移动到 `product-web` 模块,移动完后你的代码结构是这个样子。
    最详细的 Spring Boot 多模块开发与排坑指南
    多模块目录结构
  • 创建 common 模块

    选择 maven - next,填写模块名称。

    创建 web 模块

    
    modules
        moduleproduct-common/module
        moduleproduct-web/module
    /modules
    

    移动代码到指定模块

    到这里,多模块已经拆分完成了, 但是 ProductController  代码里的红色警告让你发现事情还没有结束。

    依赖管理

    处理依赖问题

    你发现了代码里的红色警告,不过你也瞬间想到了是因为把 Product  类移动到了 product-common 模块,导致这里引用不到了。

    然后你查看了下 product-common 模块的 pom.xml 里的内容。

    
    ?xml version="1.0" encoding="UTF-8"?
    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"
        parent
            artifactIdspringboot-module-demo/artifactId
            groupIdcom.wdbyte/groupId
            version0.0.1-SNAPSHOT/version
        /parent
        modelVersion4.0.0/modelVersion
        artifactIdproduct-common/artifactId
    /project
    

    机智的在 Product-web 模块的 pom.xml 里引入 product-common,手起键落,轻松搞定。

    
    ?xml version="1.0" encoding="UTF-8"?
    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"
        parent
            artifactIdspringboot-module-demo/artifactId
            groupIdcom.wdbyte/groupId
            version0.0.1-SNAPSHOT/version
        /parent
        modelVersion4.0.0/modelVersion
        artifactIdproduct-web/artifactId
        dependencies
            dependency
                groupIdcom.wdbyte/groupId
                artifactIdproduct-common/artifactId
            /dependency
        /dependencies
    /project
    

    满心欢喜的你快速的点击 Build-  Build Project,得到的 Error 警告刺痛了顶着黑眼圈的你。

    不过你还是迅速定位了问题,查看 maven 依赖,你发现是因为没有指定 product-common 依赖的版本号。

    原来如此,因为没有指定版本号,我们指定上不就完事了嘛。在最外层的主 pom.xml 中添加 dependencyManagement 添加上指定依赖和要指定的版本号。

    
        dependencyManagement
            dependencies
                dependency
                    groupIdcom.wdbyte/groupId
                    artifactIdproduct-common/artifactId
                    version0.0.1-SNAPSHOT/version!-- maven 打包默认 0.0.1-SNAPSHOT 版本 --
                /dependency
            /dependencies
        /dependencyManagement
    

    刷新 maven ,发现项目已经不报错了,编译成功,运行启动类,熟悉的 Spring logo 又出现在眼前。

    优化依赖

    是的,Spring Boot 应用在改造成多模块后成功运行了起来,但是你貌似发现一个问题,模块 common 和模块 web 都继承了主 pom ,主 pom 中有 Lombok 、Spring Boot Web 和  Spring Boot Test 依赖,而 common 模块里只用到了 Lombok 啊,却一样继承了 Spring Boot 其他依赖,看来还是要改造一把。

  • 只有 `common` 模块用到的依赖移动到 `common` 模块。
    ?xml version="1.0" encoding="UTF-8"?
    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"
        parent
            artifactIdspringboot-module-demo/artifactId
            groupIdcom.wdbyte/groupId
            version0.0.1-SNAPSHOT/version
        /parent
        modelVersion4.0.0/modelVersion
        artifactIdproduct-common/artifactId
        dependencies
            dependency
                groupIdorg.projectlombok/groupId
                artifactIdlombok/artifactId
                optionaltrue/optional
            /dependency
        /dependencies
    /project
    
  • 只有 `web` 模块用到的依赖移动到 `web` 模块。
    ?xml version="1.0" encoding="UTF-8"?
    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"
        parent
            artifactIdspringboot-module-demo/artifactId
            groupIdcom.wdbyte/groupId
            version0.0.1-SNAPSHOT/version
        /parent
        modelVersion4.0.0/modelVersion
        artifactIdproduct-web/artifactId
        
    
    
    dependencies
        dependency
            groupIdcom.wdbyte/groupId
            artifactIdproduct-common/artifactId
        /dependency
    
        dependency
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-web/artifactId
        /dependency
    
        dependency
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-test/artifactId
            scopetest/scope
            exclusions
                exclusion
                    groupIdorg.junit.vintage/groupId
                    artifactIdjunit-vintage-engine/artifactId
                /exclusion
            /exclusions
        /dependency
    /dependencies
    

    /project

  • 抽取用到的版本号到 `properties`,这里抽取 `common` 模块的依赖版本。 到这里最外层主 pom 的内容是这样的。
    ?xml version="1.0" encoding="UTF-8"?
    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 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        modelVersion4.0.0/modelVersion
        packagingpom/packaging
        modules
            moduleproduct-common/module
            moduleproduct-web/module
        /modules
        parent
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-parent/artifactId
            version2.2.5.RELEASE/version
            relativePath/ !-- lookup parent from repository --
        /parent
        groupIdcom.wdbyte/groupId
        artifactIdspringboot-module-demo/artifactId
        version0.0.1-SNAPSHOT/version
        namespringboot-module-demo/name
        descriptionDemo project for Spring Boot/description
    
    
    
    properties
        java.version1.8/java.version
        product-common.version0.0.1-SNAPSHOT/product-common.version
    /properties
    
    dependencyManagement
        dependencies
            dependency
                groupIdcom.wdbyte/groupId
                artifactIdproduct-common/artifactId
                version${product-common.version}/version
            /dependency
        /dependencies
    /dependencyManagement
    
    build
        plugins
            plugin
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-maven-plugin/artifactId
            /plugin
        /plugins
    /build
    

    /project

  • 只有 web 模块用到的依赖移动到 web 模块。

    
    ?xml version="1.0" encoding="UTF-8"?
    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"
        parent
            artifactIdspringboot-module-demo/artifactId
            groupIdcom.wdbyte/groupId
            version0.0.1-SNAPSHOT/version
        /parent
        modelVersion4.0.0/modelVersion
        artifactIdproduct-web/artifactId
        
        dependencies
            dependency
                groupIdcom.wdbyte/groupId
                artifactIdproduct-common/artifactId
            /dependency
    
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-web/artifactId
            /dependency
    
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-test/artifactId
                scopetest/scope
                exclusions
                    exclusion
                        groupIdorg.junit.vintage/groupId
                        artifactIdjunit-vintage-engine/artifactId
                    /exclusion
                /exclusions
            /dependency
        /dependencies
    /project
    

    到这里最外层主 pom 的内容是这样的。

    看似完美,重新  Build-  Build Project ,发现一切正常,运行发现一切正常,访问正常。

    打包编译

    好了,终于到了最后一步了,你感觉到胜利的曙光已经照到了头顶,反射出耀眼的光芒。接着就是 mvn package

    
    [INFO] springboot-module-demo ............................. SUCCESS [  2.653 s]
    [INFO] product-common ..................................... FAILURE [  2.718 s]
    [INFO] product-web ........................................ SKIPPED
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 6.084 s
    [INFO] Finished at: 2020-03-19T08:15:52+08:00
    [INFO] Final Memory: 22M/87M
    [INFO] ------------------------------------------------------------------------
    [ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.5.RELEASE:repackage (repackage) on project product-common: Execution repackage of goal org.springframework.boot:spring-boot-m
    aven-plugin:2.2.5.RELEASE:repackage failed: Unable to find main class - [Help 1]
    [ERROR]
    

    ERROR 让你伤心了,但是你还是从报错中寻找到了一些蛛丝马迹,你看到是  spring-boot-maven-plugin 报出的错误。重新审视你的主 pom 发现 build 编译插件用到了 spring-boot-maven-plugin。

    
        build
            plugins
                plugin
                    groupIdorg.springframework.boot/groupId
                    artifactIdspring-boot-maven-plugin/artifactId
                /plugin
            /plugins
        /build
    

    略加思索后将这段移动到 web 模块的 pom,因为这是 Spring Boot 的打包方式,现在放在主 pom 中所有的模块都会继承到,那么对于 common 模块来说是肯定不需要的。

    移动后重新打包,不管你是运行命令 mvn package 还是双击 IDEA 中的 maven 管理中的 package ,想必这时候你都已经打包成功了

    web 模块下的目录 target 里也可以看到打包后的 jar 文件 product-web-0.0.1-SNAPSHOT.jar。可以使用 java 命令直接运行。

    
    $ springboot-module-demoproduct-webtargetjava -jar product-web-0.0.1-SNAPSHOT.jar
    
      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |___, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.5.RELEASE)
    
    2020-03-19 08:33:03.337  INFO 15324 --- [           main] com.wdbyte.Application                   : Starting Application v0.0.1-SNAPSHOT on DESKTOP-8SCFV4M with PID 15324 (C:Users83981Desktopspringboot-mod
    ule-demoproduct-webtargetproduct-web-0.0.1-SNAPSHOT.jar started by 83981 in C:Users83981Desktopspringboot-module-demoproduct-webtarget)
    2020-03-19 08:33:03.340  INFO 15324 --- [           main] com.wdbyte.Application                   : No active profile set, falling back to default profiles: default
    2020-03-19 08:33:04.410  INFO 15324 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    2020-03-19 08:33:04.432  INFO 15324 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-03-19 08:33:04.432  INFO 15324 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
    2020-03-19 08:33:04.493  INFO 15324 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-03-19 08:33:04.493  INFO 15324 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1107 ms
    2020-03-19 08:33:04.636  INFO 15324 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-03-19 08:33:04.769  INFO 15324 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2020-03-19 08:33:04.772  INFO 15324 --- [           main] com.wdbyte.Application                   : Started Application in 1.924 seconds (JVM running for 2.649)
    2020-03-19 08:33:07.087  INFO 15324 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
    

    想必少了点什么,多模块不仅为了结构清晰,更是为了其他项目可以复用模块(如 common 模块),现在这个时候如果你新打开了一个项目,依赖 common  发现是引用不到的,因为你需要把模块安装到本地仓库。可以点击 IDEA - Maven - install,也可以通过 maven 命令。

    
    # -Dmaven.test.skip=true 跳过测试
    # -U 强制刷新
    # clean 清理缓存
    # install 安装到本地仓库
    $ springboot-module-demo mvn -Dmaven.test.skip=true -U clean install
    

    重新引入发现没有问题了。

    文中代码已经上传到 Github:niumoo/springboot

    END

    Java面试题专栏

    最详细的 Spring Boot 多模块开发与排坑指南

    我知道你 “在看最详细的 Spring Boot 多模块开发与排坑指南

    原文始发于微信公众号(Java知音):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 最详细的 Spring Boot 多模块开发与排坑指南


     上一篇
    在SpringBoot中使用Spring Session解决分布式会话共享问题 在SpringBoot中使用Spring Session解决分布式会话共享问题
    作者:简单的土豆 www.jianshu.com/p/e4191997da56 前言如果你正在使用Java开发Web应用,想必你对HttpSession非常熟悉,但我们知道HpptSession默认使用内存来管理Sess
    下一篇 
    SpringBoot 整合 Dubbo实践(实用文章) SpringBoot 整合 Dubbo实践(实用文章)
    作者:Cott cnblogs.com/cott/p/12373043.html 1.选择ZooKeeper作为注册中心在linux环境中使用docker安装ZooKeeper //拉取zookeeper镜像 dock