多模块 SpringBoot 种子工程搭建 (一)

1. 搭建准备&使用场景

  • IDE: InteliJ IDEA
  • JDK: 1.8
  • SpringBoot: 2.0.3.RELEASE
  • 使用场景: 中小型项目, API 和后台管理 分开部署

2. 模块说明

@模块依赖关系图 | center | 400x200
  • config: 配置中心模块
  • bizz-support: 业务支持模块, 存放根据表生成的通用 Service, Mapper
  • framework-support: 框架支持模块
  • api: API 接口模块
  • manage: 后台管理模块

3. 模块搭建

3.1 parent

  1. 使用 IDEA 或者 官网的 Spring initializr创建一个空项目multi-module-springboot, 删除多余文件.
  2. pom.xml 中将 packaging 改成 pom 提供 依赖版本管理
  3. <dependencies> 标签外面嵌套一层 <dependencyManagement>
  4. 删除 <build>

3.2 api

1. Spring initializr 创建一个项目, 父模块引入 spring-boot-starter-web, api 对应配置

且设置父模块为multi-module-springboot

2. 新建 Controller


@RestController
@RequestMapping("/api")
public class HelloRestfulController {

    @GetMapping({"/", "index", "index.html"})
    public Map adminIndex() throws Exception {

        return new HashMap(3) {{
            put("code", 0);
            put("message", "成功");
            put("data", "Hello World!");
        }};
}

3. 测试完成

$ curl http://localhost:8766/api/
{"message":"成功","data":"Hello World!","code":0}

3.3 manage

1. 如创建 3.2 api 模块一样创建 manage 模块

2. 添加 thymeleaf 依赖

<springboot.version>2.0.3.RELEASE</springboot.version>
<nekohtml.version>1.9.22</nekohtml.version>
<thymeleaflayout.version>2.3.0</thymeleaflayout.version>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
 <!--<editor-fold desc="THYMELEAF 页面模板引擎">-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-thymeleaf</artifactId>
     <version>${springboot.version}</version>
     <!-- 排除掉默认的版本 -->
     <exclusions>
         <exclusion>
             <groupId>org.thymeleaf</groupId>
             <artifactId>thymeleaf</artifactId>
         </exclusion>
         <exclusion>
             <groupId>org.thymeleaf</groupId>
             <artifactId>thymeleaf-spring5</artifactId>
         </exclusion>
     </exclusions>
 </dependency>
 <dependency>
     <groupId>net.sourceforge.nekohtml</groupId>
     <artifactId>nekohtml</artifactId>
     <version>${nekohtml.version}</version>
 </dependency>
 <dependency>
     <groupId>nz.net.ultraq.thymeleaf</groupId>
     <artifactId>thymeleaf-layout-dialect</artifactId>
     <version>${thymeleaflayout.version}</version>
 </dependency>
 <dependency>
     <groupId>org.thymeleaf</groupId>
     <artifactId>thymeleaf-spring5</artifactId>
     <version>${thymeleaf.version}</version>
 </dependency>
 <dependency>
     <groupId>org.thymeleaf</groupId>
     <artifactId>thymeleaf</artifactId>
     <version>${thymeleaf.version}</version>
 </dependency>
 <!--</editor-fold>-->

3. resources 下面新建 templates/home.html

4. application.properties 配置 thymeleaf

spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

5. 新建 Controller

package com.logictech.manage;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author JG.Hannibal
 * @since 2018/7/4 14:25
 */
@Controller
public class HelloViewController {

    @GetMapping({"/hello"})
    public String adminIndex(ModelMap model) throws Exception {
        model.addAttribute("text", "Hello World!");
        return "home";
    }

}

6. 测试完成

$ curl http://localhost:8765/hello
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>Hello World!</div>
<div>Hello World!</div>
</body>
</html>

3.4 config

1. 工程右键, 新建 Maven 子模块 config (该次不是Spring initializr), 因为该工程作为 resources root, 所以将除了pom.xml 的文件全部删除.

2. 新建 profile 文件夹, 便于不同环境的发布部署

cd config
vi src/main/resources/dev/application.properties src/main/resources/prod/application.properties src/main/resources/test/application.properties

3. 每个application中加入配置内容

#每个配置文件自己的 profile, 例: env=dev
env=* 
#这边端口每个配置文件不一样就可以
server.port=876* 
# 之前配置的模板引擎的配置一致
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

该版本对其他编辑器支持度太差,优化为由Maven实现该功能。详细见4.
4. IDEA设置为 resources root

Alt text

4.使用 maven-remote-resources-plugin 进行Maven的配置文件模块分离

4.1 config模块内pom.xml添加 maven-remote-resources-plugin 插件,配置如下:

<build>
        <plugins>
            <plugin>
                <artifactId>maven-remote-resources-plugin</artifactId>
                <version>1.7.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>bundle</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.yml</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

在config目录下执行命令mvn clean package,见编译后的文件是否有/classes/META-INF/maven/remote-resources.xml,有则添加插件成功。最终编译目录如下:

├── classes
│   ├── META-INF
│   │   └── maven
│   │       └── remote-resources.xml
│   ├── dev
│   │   ├── application.properties
│   │   ├── log4j2.yml
│   │   └── message
│   │       └── messages.properties
│   ├── prod
│   │   ├── application.properties
│   │   ├── log4j2.yml
│   │   └── message
│   │       └── messages.properties
│   └── test
│       ├── application.properties
│       ├── log4j2.yml
│       └── message
│           └── messages.properties

4.2 apimanage 模块进行 远程配置模块 消费的配置,在对应模块下pom.xml添加以下内容,resourceBundle 的值为你的config包的groupId:artifactId:version(应该不会有人把我写的老老实实填进去吧...)


            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-remote-resources-plugin</artifactId>
                <version>1.7.0</version>
                <configuration>
                    <resourceBundles>
                        <resourceBundle>com.logictech:config:${project.version}</resourceBundle>
                    </resourceBundles>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

随后,还是走一步测一步 mvn clean package,见编译后目录,若含有maven-shared-archive-resources 目录则添加插件成功,然后查看classes目录,也可以看见 config下的resources也添加进来了。结果如下图

/Users/JG.Hannibal/Projects/multi-module-springboot/manage/target
├── classes
│   ├── dev
│   │   ├── application.properties
│   │   ├── log4j2.yml
│   │   └── message
│   ├── prod
│   │   ├── application.properties
│   │   ├── log4j2.yml
│   │   └── message
│   ├── templates
│   │   └── home.html
│   └── test
│       ├── application.properties
│       ├── log4j2.yml
│       └── message

这边存在一个问题,springboot 默认是使用classpath下面路径下的配置文件的,这边比标准工程多了一级${profile},这边处理方案很多,不认为我的方案为最佳方案,所以这里还是存疑,欢迎讨论建议,有最终答案后会再更新文章!

5. 测试, 将 api 工程中resources/application.properties 删除, 依赖引入 config, 改写 Controller, 将 env 打印出来.

@RestController
@RequestMapping("/api")
public class HelloRestfulController {

    @Value("${env}")
    private String env;


    @GetMapping({"/", "index", "index.html"})
    public Map adminIndex() throws Exception {

        return new HashMap(3) {{
            put("code", 0);
            put("message", "成功");
            put("data", "This Active Profile is: [" + env + "]");
        }};
    }

}

6. 修改启动环境变量, 将 config 路径配置到我们的子模块内,测试完成.

java -jar api-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev 
--spring.config.location=classpath:${spring.profiles.active}/
$ curl http://localhost:8765/api/
{"message":"成功","data":"This Active Profile is: [dev]","code":0}

manage 模块的改造和 api 一致

存在一个问题, 即 运行 api 模块时, Maven 找不到 config-0.0.1-SNAPSHOT.jar
只需要到 Maven 界面, 将 Nexus的勾选去掉即可.

3.5 framework-support

1. 新建 Maven 子模块 framework-support, 添加spring-boot-starter-web依赖, config 依赖

这边我们先实现 Controller 的一个 AOP, 所以引入spring-aop 包括AspectJ

<!--<editor-fold desc="AOP">-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>${springboot.version}</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${aspectj.version}</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
</dependency>
<!--</editor-fold>-->

2. 编写 ControllerLoggerAop

3. 将 framework-support 的包 添加 到 API 的主函数ComponentScan 里去, API 添加 framework-support依赖.

4. 测试完成

$ curl http://localhost:8766/api/
{"message":"成功","data":"This Active Profile is: [prod]","code":0}
##########|    URL : http://localhost:8766/api/
##########|    HTTP_METHOD : GET
##########|    IP : 127.0.0.1
##########|    CLASS_METHOD : com.logictech.api.HelloRestfulController.adminIndex
##########|    ARGS : []
====================================================
##########|    RESPONSE : {message=成功, data=This Active Profile is: [prod], code=0}
====================================================

3.6 bizz-support

1. 创建Maven子模块 bizz-support, 根据需要创建依赖

2. 创建 CommonConst

public class CommonConst {
    public static final String PROJECT_NAME = "MULTI_MODULE_SPRING_BOOT";
}
    

3. api 模块改写 Controller, 测试完成

@RestController
@RequestMapping("/api")
public class HelloRestfulController {

    @Value("${env}")
    private String env;


    @GetMapping({"/", "index", "index.html"})
    public Map adminIndex() throws Exception {

        return new HashMap(3) {{
            put("code", 0);
            put("message", "成功");
            put("data", "This Active Profile is: [" + env + "], Project Name: [" + PROJECT_NAME + "]");
        }};
    }

}
$ curl http://localhost:8766/api/
{"message":"成功","data":"This Active Profile is: [prod], Project Name: [MULTI_MODULE_SPRING_BOOT]","code":0}

4. 总结

@模块依赖关系图 | center | 400x200

还是就最开始那张图来讲, 该工程分为大体分为三块:

  • config: 配置中心, 可供后期改造成分布式的配置中心
  • support: 支持中心, 核心模块, framework-support 和 bizz-support 其实可以放一起
    但是更清晰一点,后期可以把 bizz-support 改造成 rbc服务.
  • business: 业务模块, 展现层

相同的层级的配置可以类比配置.
另外, 写得没有面面俱到, 多模块工程V0.0.1版本, 后期会继续完善.


仓库地址: https://github.com/NibiewJ/multi-module-springboot

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容