4、Spring Boot项目初始化:创建父工程、创建子模块、配置多环境profile、整合MyBatis-Plus
好,咱们正式开始动手了。
前面几章我们把秒杀系统的整体架构、数据流向、核心难点都捋了一遍。现在,该把袖子撸起来,真正开始写代码了。
我个人习惯,不管项目大小,先搭好工程骨架。骨架稳了,后面加功能才不慌。这一章我们就来做这件事——初始化一个Spring Boot项目,并且把秒杀系统需要的几个核心模块拆分好。
4.1 为什么用多模块?
你想想看,秒杀系统不是一个小玩具。它包含用户登录、商品管理、订单处理、秒杀核心逻辑、消息推送等等。如果全写在一个工程里,后期维护就是噩梦。
我在项目中遇到过,一个单体应用到了后期,光启动就要3分钟,改一行代码要重新编译整个项目。那感觉,嗯,谁用谁知道。
所以,我们采用Maven多模块结构。父工程只做依赖管理和版本控制,子模块各司其职。
核心原则:父工程不写业务代码,只做pom管理。子模块之间通过依赖引用,避免循环依赖。
4.2 创建父工程
我们先创建一个普通的Maven项目作为父工程。记住,父工程的packaging一定要是pom。
<groupId>com.seckill</groupId>
<artifactId>seckill-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
这里我选了Spring Boot 2.7.12,为什么不是3.x?因为3.x要求JDK17,而很多生产环境还在用JDK8或11。咱们做秒杀系统,稳定第一。
小技巧:在父工程的<dependencyManagement>里统一管理所有子模块的版本号。这样升级依赖时,改一处就行。
4.3 创建子模块
接下来,我们创建四个核心子模块。每个模块对应一个独立的Maven子项目。
| 模块名 | 职责 | 关键依赖 |
|---|---|---|
| seckill-common | 公共工具类、常量、统一返回对象 | lombok, hutool |
| seckill-dao | 数据访问层,MyBatis-Plus映射 | mybatis-plus-boot-starter, mysql-connector |
| seckill-service | 业务逻辑层,事务、缓存 | seckill-dao, redis, spring-tx |
| seckill-web | 控制器层,接收请求、返回结果 | seckill-service, spring-boot-starter-web |
在父工程的pom.xml里,用<modules>标签把它们聚合起来:
<modules>
<module>seckill-common</module>
<module>seckill-dao</module>
<module>seckill-service</module>
<module>seckill-web</module>
</modules>
每个子模块的pom.xml里,<parent>指向父工程。举个例子,seckill-dao的配置:
<parent>
<groupId>com.seckill</groupId>
<artifactId>seckill-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
注意:子模块之间的依赖关系要理清楚。seckill-web依赖seckill-service,seckill-service依赖seckill-dao,seckill-dao依赖seckill-common。不要搞成循环依赖,否则Maven会报错。
4.4 配置多环境Profile
开发、测试、生产,这三个环境的配置肯定不一样。数据库地址、Redis密码、日志级别,都得分开。
Spring Boot的Profile机制就是干这个的。我们在seckill-web模块的src/main/resources下创建三个配置文件:
application-dev.yml—— 开发环境,数据库连本地,日志级别DEBUGapplication-test.yml—— 测试环境,数据库连测试服务器,日志级别INFOapplication-prod.yml—— 生产环境,数据库连线上,日志级别WARN,开启性能监控
举个例子,application-dev.yml的内容:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
logging:
level:
com.seckill: DEBUG
而application-prod.yml则要谨慎得多:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://prod-db:3306/seckill?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: seckill_prod
password: ${DB_PASSWORD} # 生产密码从环境变量读取
hikari:
maximum-pool-size: 20
minimum-idle: 5
logging:
level:
com.seckill: WARN
启动时,通过--spring.profiles.active=dev来指定环境。我一般会在IDE里配好启动参数,部署时用脚本传参。
避坑指南:我曾经在生产环境忘记改Profile,结果连到了开发数据库,把测试数据写进去了。从那以后,我强制要求所有部署脚本必须显式指定Profile,并且加了一个启动检查——如果Profile是dev但IP不是本地,直接报错退出。
4.5 整合MyBatis-Plus
MyBatis-Plus,说白了就是MyBatis的增强版。它帮我们省去了写大量XML映射文件的痛苦。
在seckill-dao模块里,引入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
然后,在seckill-dao里创建一个配置类,开启MyBatis-Plus的扫描:
@Configuration
@MapperScan("com.seckill.dao.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件,秒杀系统的订单列表查询会用到
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
接下来,我们创建一个实体类和Mapper接口。以秒杀商品为例:
@Data
@TableName("seckill_goods")
public class SeckillGoods {
@TableId(type = IdType.AUTO)
private Long id;
private String goodsName;
private Integer stockCount;
private BigDecimal seckillPrice;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
public interface SeckillGoodsMapper extends BaseMapper<SeckillGoods> {
// 这里什么都不用写,BaseMapper已经提供了CRUD方法
// 如果需要复杂查询,可以在这里定义接口方法,配合XML或注解
}
你看,一个Mapper接口,继承BaseMapper,就有了insert、selectById、updateById、deleteById这些基础方法。是不是很省事?
核心要点:MyBatis-Plus的BaseMapper提供了单表CRUD,但秒杀系统的库存扣减不能用它自带的updateById。为什么?因为并发场景下,我们需要用update ... set stock = stock - 1 where stock > 0这样的原子操作。这个后面讲秒杀核心逻辑时会详细说。
4.6 验证项目结构
到这里,我们的项目骨架应该长这样:
seckill-parent
├── seckill-common
│ └── src/main/java/com/seckill/common
│ ├── constant
│ ├── exception
│ └── response
├── seckill-dao
│ └── src/main/java/com/seckill/dao
│ ├── config
│ ├── entity
│ └── mapper
├── seckill-service
│ └── src/main/java/com/seckill/service
│ ├── impl
│ └── SeckillGoodsService.java
└── seckill-web
└── src/main/java/com/seckill/web
├── controller
└── SeckillApplication.java // 启动类
启动类放在seckill-web里,加上@SpringBootApplication注解:
@SpringBootApplication
public class SeckillApplication {
public static void main(String[] args) {
SpringApplication.run(SeckillApplication.class, args);
}
}
启动后,访问http://localhost:8080/,能看到Spring Boot的默认错误页面,说明项目启动成功了。
注意:如果启动报错说找不到数据源,检查一下application.yml里是否配置了数据库连接。或者,你可以先注释掉MyBatisPlusConfig里的数据源相关配置,等数据库准备好了再打开。
4.7 本章小结
嗯,这一章我们做了几件基础但重要的事:
- 创建了Maven多模块父工程,统一管理依赖
- 拆分了四个子模块:common、dao、service、web
- 配置了多环境Profile,开发、测试、生产各有一套配置
- 整合了MyBatis-Plus,实现了基础的CRUD能力
项目骨架搭好了,下一章我们就可以开始写真正的业务代码了。秒杀系统的核心——库存扣减、限流、防超卖,这些硬骨头,我们一个一个啃。
公众号:蓝海资料掘金营,微信deep3321