SpringBoot多模块创建,业务应用以及属性拓展

时间:2024-11-18 18:06:36

多模块与分布式

首先要明确一点,多模块和分布式是两种不同的概念,不能混淆,不然会被人笑到你35岁退休的。


多模块:顾名思义,一个整体项目拆分不同的模块,具体什么模块呢?

例如:传统SpringBoot应用从构建到Application运行,这就是个单体应用,也就是我们的pojo,controller,service以及dao是存在一个根src下,如图

SpringBoot多模块创建,业务应用以及属性拓展_多模块

大家统一以一个Maven(包管理)控制,编译会被统一打入jar/war中。

多模块实际就是单体应用的演变,说是演变,其实还够不上演变,因为演变这个词包含了创新变革,实际就是通过Maven的控制形成小小的模块管理,只能说是创新,并不能说是变革。

如图所示:

SpringBoot多模块创建,业务应用以及属性拓展_多模块_02

  • 多模块中,我们把每个业务流程差分为一个模块,每个模块由每个模块各自的Maven进行版本控制,当前模块之前也是可以相许引用的。
  • 分布式:顾名思义,相对于传统的多模块管理,分布式主要区分的是业务场景,并非的流程场景。

什么是流程场景呢?

SpringBoot多模块创建,业务应用以及属性拓展_apache_03

如上图所示,controller(控制器)、service(业务处理)、DAO(数据处理)以及数据,这个就是流程,这也是我们习惯的流程体系,至于为什么是三层设计,可以百度了解一下。

什么是业务场景呢?

SpringBoot多模块创建,业务应用以及属性拓展_spring_04

如上图所示,无论是C端还是B端,他们提供的都是服务,包括登录,注册,购买商品,商品页面展示等等,这些都是提供的服务,架构师我们考虑到百万级乃至千万级流量可能会拖垮单体服务,于是就把不同的业务拆分出来。部署在不同的服务器节点上,形成集群,集群之间相互通信。

当然我以上说的都很笼统,细致来说里面的技术层面有很多的应用,包括RPC通讯框架,分布式集群资源共享,熔断机制,服务治理,服务注册太多了,这就是我们面临的大环境,技术层出不穷,产品迭代快的一批。

那么我们说的多模块和分布式的区别到底是什么呢?

从业务和流程两个角度出发,从系统架构出发,分布式是实现业务服务拆分,多模块是实现流程拆分。

Maven多模块创建

上面我从个人的理解上面阐述了多模块和分布式的概念,仅仅是个人理解啊,大佬勿喷。
我是用SpringBoot创建的管理,不是用空Maven项目创建的,两者之前有什么区别呢?我的答案是有一点区别,但是不大,基本可以忽略不计。

创建SpringBoot父项目

SpringBoot多模块创建,业务应用以及属性拓展_多模块_05

这里没用系统镜像,我用的是阿里云镜像

https://start.aliyun.com/

SpringBoot多模块创建,业务应用以及属性拓展_多模块_06

groupid和artifactId都统称为“坐标”,是为了保证项目唯一性而提出的。

配置项勾选中我们什么都不需要勾选直接创建

SpringBoot多模块创建,业务应用以及属性拓展_apache_07

创建完成后,红框勾选的都不需要了,因为父级项目只是一个空壳子,用于模块之前之间依赖传递和版本控制

SpringBoot多模块创建,业务应用以及属性拓展_apache_08

创建子模块

创建之前我们先对系统进行模块设计

  • common 公共模块:包含工具类,枚举定义,静态变量等
  • config 配置模块:包括AOP切面,数据源,缓存等等一些列配置配置类容
  • model 模型模块:包含POJO,VO,BO
  • dao 数据层模块:包含mapper映射文件,SQL语句XML(这里我选用的MyBatis),如果选用JAP这类的ORM框架的,这个模块可以省去,但是使用自定义SQL,JAP可以选择兼容Freemaker模板引擎
  • service 业务模块:包含用户业务等一些具体的业务处理
  • console 控制台模块:可以理解为controller对外开放接口
  • api 开放接口模块:可以理解微信QQ第三方登录接口,OAuth2.0协议规范接口

有人的问,为啥不把api 和console放在一起呢?为什么呢?不是很LOW吗?开放和不开放的接口尽量不要放在一起,在应对AOP切面权限校验的时候,这两套接口类型肯定校验的方式和机制是不一样的,这样耦合性是不是就很高了嘛,而且代码累积一个模块下,干嘛不根据业务种类的不同去区分他们呢!

SpringBoot多模块创建,业务应用以及属性拓展_apache_09

新建模块

SpringBoot多模块创建,业务应用以及属性拓展_apache_10

下一步

SpringBoot多模块创建,业务应用以及属性拓展_apache_11

这里需要做区分了,不是随便写写的了,你瞎几把乱写,等着自己原地爆炸吧。。。。

groupid和artifactId都统称为“坐标”,既然是坐标,那就是确保他的唯一性

首先父类是com.personloger,我们这里就具体到模块com.personloger.common

artifactId就是模块的名称common

package,包路径我们为了做区分和groupid保持一致

这个模块因为我们设定只存放一些公共使用的枚举变量,公共方法工具类,因此不需要引入配置

SpringBoot多模块创建,业务应用以及属性拓展_spring_12

直接创建
config 、model、service、dao,console,api 有是重复上面的创建步骤

多模块配置Maven

配置父级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">
    <modelVersion>4.0.0</modelVersion>
    <!-- 定义SpringBoot 继承关系以及版本 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <groupId>com.personloger</groupId>
    <artifactId>only-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>only-api</name>
    <description>Demo project for Spring Boot</description>

    <!-- 定义打包方式为pom -->
    <packaging>pom</packaging>

    <!-- 子模块 -->
    <modules>
        <module>console</module>
        <module>api</module>
        <module>service</module>
        <module>model</module>
        <module>common</module>
        <module>dao</module>
        <module>config</module>
    </modules>

    <!-- 版本控制 以及父子配置-->
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>

        <spring-boot.version>${parent.version}</spring-boot.version>
        <fastjson.version>2.0.13</fastjson.version>
        <mybatis.version>2.1.4</mybatis.version>
        <maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
        <maven.surefire.plugin.version>2.22.2</maven.surefire.plugin.version>
        <hutool.version>5.8.6</hutool.version>
        <springfox.swagger2.version>2.9.2</springfox.swagger2.version>
        <org.redisson.version>3.16.0</org.redisson.version>
        <com.google.guava.version>30.1-jre</com.google.guava.version>
        <com.google.zxing.version>3.5.0</com.google.zxing.version>
        <dingtalk.version>2.0.0</dingtalk.version>
        <org.apache.commons.collections4.version>4.4</org.apache.commons.collections4.version>
        <org.apache.commons.lang3.version>3.12.0</org.apache.commons.lang3.version>
    </properties>

    <dependencies>
        <!-- 类工具包 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${org.apache.commons.lang3.version}</version>
        </dependency>
        <!-- 集合工具包 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>${org.apache.commons.collections4.version}</version>
        </dependency>
        <!-- swagger2.0 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.swagger2.version}</version>
        </dependency>
        <!-- Hutool 工具集合 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- SpringBoot Base dependency-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.personloger.OnlyApiApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

具体类容看注释

配置子模块POM

首先我们挨个打开子模块POM文件,以api为例

<?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">
    <modelVersion>4.0.0</modelVersion>

    <!-- 继承父POM -->
    <parent>
        <groupId>com.personloger</groupId>
        <artifactId>only-api</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
	<!-- 定义打包方式 -->
    <packaging>jar</packaging>

    <groupId>com.personloger.api</groupId>
    <artifactId>api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
       <!-- 清空存在依赖 -->
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.personloger.api.ApiApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

其他的模块是这么写

删除不需要的启动项以及application.properties文件

因为我们在设计之初,只开放两个web,因此除了console和api,其他的模块下的启动项目全部删除
同时我们要删除该子模块的pom关于插件的配置

			<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.personloger.model.ModelApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

这个插件删除了,因为你已经没有启动项了,这个东西实际就没有存在的意义了

同时所有模块的properties文件全部删除


配置子模块相互依赖关系

上面步骤处理完成后,我们看一下配置POM文件中,我在dependencies中写了很多开源依赖,这些都是子模块需要用的,因此我在中定义统一版本,便于统一版本管理

  	<!-- 子模块 -->
    <modules>
        <module>console</module>
        <module>api</module>
        <module>service</module>
        <module>model</module>
        <module>common</module>
        <module>dao</module>
        <module>config</module>
    </modules>

这里是子模块定义,你不定义,通过pom方式打包,就会出现依赖找不到的问题。切记,模块之间依赖并不是这个生效的,这里只是告诉maven,我打包或者编译的时候,请把这些子模块一起顺带手一起打印了。

那么子模块之间相互依赖,我们该怎么配置呢?

其实很简单,在配置子模块POM的时候,我把原有的全部清空了,一方面是告诉大家,父级POM用于公共依赖管理,子模块只需要引用需要模块即可


例如

model模块要贯穿整个项目,因此,我们在每个子模块都是引入这个依赖(自身不需要引用)

SpringBoot多模块创建,业务应用以及属性拓展_apache_13

这样我们在model这个模块创建的类,在其他模块均可以使用的到,可以测试一下吆。

子模块依赖设计


  • model需要配置到其他模块,自身不需要
  • console和api需要service业务层处理,因此我们把service配置到console和api模块
  • service业务层模块需要依赖dao模块,因为业务层下来就是持久化层
  • common公共模块,因为是业务处理,所以直接给service即可

配置多环境信息

我默认是选在console模块下创建application.yml文件的,你可以选在api,或者自己新建一个web模块专门用于管控application文件,系统默认创建的是properties文件,与yml文件还有区别的,具体的区别就不介绍了,yml的树形结构看着直观一点

  • application.yml
# 定义两种环境模式 dev prod
# dev 本地运行环境
# prod 线上环境,有的公司配备了流水线,自然会有测试环境
spring:
  application:
    name: console
  profiles:
    active: dev
  # 对于一些公共配置我们可以配置在这里面,dev 与 prod主要是为了区分线上和线下的不同,没必要相同的配置写N遍
  # 例如静态资源访问路径
  resources:
    static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
  # 模板引擎
  thymeleaf:
    prefix: classpath:/templates/
    encoding: UTF-8
    cache: false
    suffix: .html
    servlet:
      content-type: text/html
  • application-dev.yml
# 测试环境端口
server:
  port: 8080
# 本地环境
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/pmsc?useUnicode=true&characterEncoding=utf8&useSLL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    hikari:
      auto-commit: true
      idle-timeout: 60000
      connection-timeout: 60000
      max-lifetime: 0
      minimum-idle: 10
      maximum-pool-size: 15
  • application-prod.yml
# 测试环境端口
server:
  port: 80
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/pms?useUnicode=true&characterEncoding=utf8&useSLL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: username
    password: password
    hikari:
      auto-commit: true
      idle-timeout: 60000
      connection-timeout: 60000
      max-lifetime: 0
      minimum-idle: 10
      maximum-pool-size: 15

我们来测试一下启动

SpringBoot多模块创建,业务应用以及属性拓展_spring_14

SpringBoot多模块创建,业务应用以及属性拓展_spring_15