4. 项目工程初始化:搭建你的第一个券商中台模块
好,咱们正式开始动手了。
前面三章我们聊了架构设计、技术选型、还有那些让人头疼的合规问题。现在,是时候把想法落地成代码了。我个人习惯,一个新项目的第一步,不是急着写业务逻辑,而是先把工程骨架搭好。就像盖房子,你得先有地基和框架,才能往里面填砖头。
这一章,我们就来初始化整个项目。说白了,就是创建父工程,然后把几个核心模块——common、util、entity、feign——一个个建起来。最后,再配上启动类和配置文件。
4.1 为什么需要多模块?
你可能会问:为什么不能把所有代码都写在一个项目里?
嗯,我刚开始做开发时也这么干过。结果呢?项目越来越大,编译一次要五分钟,改个工具类都得全量部署。更别提多人协作时,代码冲突能让你怀疑人生。
多模块的好处很明显:
- 职责分离: 每个模块只干一件事,比如 util 只放工具类,entity 只放实体。
- 复用性: common 模块可以被多个服务引用,不用重复造轮子。
- 编译加速: 改哪个模块就编译哪个,不用全量构建。
我在券商项目中遇到过,一个交易中台有十几个微服务。如果没有多模块管理,光是依赖版本冲突就能让你加班到凌晨。所以,这一步别偷懒。
4.2 创建父工程
父工程,说白了就是一个“管家”。它不写业务代码,只负责管理所有子模块的依赖版本和插件配置。
我们先创建一个 Maven 项目,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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.trader</groupId>
<artifactId>trade-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Trade Parent</name>
<description>券商交易中台 - 父工程</description>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.1.5</spring-boot.version>
<spring-cloud.version>2022.0.4</spring-cloud.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
</properties>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>trade-common</module>
<module>trade-util</module>
<module>trade-entity</module>
<module>trade-feign</module>
<module>trade-bootstrap</module>
</modules>
</project>
4.3 公共模块(common)
common 模块放什么?说白了,就是那些“到处都能用”的东西。
- 统一的返回结果类(比如 Result
) - 全局异常处理器
- 常量定义
- 一些基础的工具类
举个例子,返回结果类:
package com.trader.common.result;
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.code = 200;
result.message = "success";
result.data = data;
return result;
}
public static <T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<>();
result.code = code;
result.message = message;
return result;
}
// getter/setter 省略
}
这个类,几乎每个服务都会用到。放在 common 里,所有子模块都能直接引用。
4.4 工具模块(util)
util 模块,顾名思义,放工具类。但要注意,别把什么都往里塞。
我建议只放那些与业务无关的通用工具,比如:
- 日期处理工具(DateUtils)
- 字符串处理工具(StringUtils)
- 加密工具(比如 AES、RSA 工具类)
- 分布式 ID 生成器
举个例子,一个简单的 ID 生成器:
package com.trader.util;
import java.util.UUID;
public class IdGenerator {
public static String generateId() {
return UUID.randomUUID().toString().replace("-", "");
}
}
4.5 实体模块(entity)
entity 模块,放数据库表对应的实体类。比如:
package com.trader.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@TableName("t_order")
public class Order {
@TableId
private Long orderId;
private String userId;
private String stockCode;
private BigDecimal price;
private Integer quantity;
private String direction; // BUY / SELL
private String status;
private LocalDateTime createTime;
// getter/setter 省略
}
这里有个小细节:实体类最好和数据库字段一一对应。别偷懒用 Map 代替,后期维护会想哭的。
4.6 API 模块(feign)
feign 模块,放的是服务间调用的接口定义。说白了,就是告诉别的服务:“我能提供这些接口,你直接调就行。”
举个例子:
package com.trader.feign;
import com.trader.common.result.Result;
import com.trader.entity.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "trade-order-service", path = "/order")
public interface OrderFeignClient {
@GetMapping("/{orderId}")
Result<Order> getOrderById(@PathVariable("orderId") Long orderId);
}
为什么要单独抽一个 feign 模块?因为这样,调用方和服务提供方都引用同一个 jar 包,接口定义保持一致。不会出现“你改了接口,我还不知道”的情况。
4.7 启动类与配置文件
最后,我们创建一个 bootstrap 模块,作为整个应用的启动入口。
启动类:
package com.trader.bootstrap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.trader.feign")
public class TradeApplication {
public static void main(String[] args) {
SpringApplication.run(TradeApplication.class, args);
}
}
配置文件 application.yml:
server:
port: 8080
spring:
application:
name: trade-bootstrap
datasource:
url: jdbc:mysql://localhost:3306/trade_db?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.trader.entity
4.8 模块依赖关系图
为了让你更直观地理解这些模块之间的关系,我画了一张图:
这张图展示了模块间的依赖关系。父工程在最上面,下面是四个子模块。bootstrap 作为启动入口,依赖 feign 模块,feign 又依赖 entity。common 和 util 则被所有模块共享。
4.9 小结
好了,这一章我们完成了项目工程初始化。你学会了:
- 创建父工程,统一管理依赖版本
- 搭建 common、util、entity、feign 四个核心模块
- 编写启动类和配置文件
嗯,这一步虽然看起来简单,但它是整个中台系统的基石。我在多个项目中都坚持这种模块划分方式,事实证明,后期维护和扩展会轻松很多。
下一章,我们会开始搭建网关和注册中心。到时候,这些模块就能真正跑起来了。