1. 项目架构设计与模块划分
《苍穹外卖》项目采用典型的多模块Maven架构,整体结构清晰、职责分明。这种设计模式在现代Java企业级应用中非常普遍,能够有效解决代码复用、模块解耦等问题。下面我们来详细拆解这个架构设计的核心要点。
1.1 父项目与子模块关系
项目根目录为my-cqwm(父项目),下设三个核心子模块:
sky-common:公共模块sky-pojo:实体类模块sky-server:业务服务模块
这种父子模块结构通过Maven的<modules>和<parent>标签实现依赖管理。父pom.xml中会定义公共依赖版本、插件配置等,子模块继承这些配置并添加自己的特定依赖。
实际开发中建议在父pom中使用
<dependencyManagement>统一管理依赖版本,避免子模块间版本冲突
1.2 sky-common模块详解
作为基础工具模块,sky-common包含以下典型内容:
- 通用工具类:如StringUtils、DateUtils等静态方法工具类
- 异常处理:自定义异常体系(BusinessException等)
- 基础组件:
- 统一返回结果封装(Result
) - 分页查询参数封装
- 常量定义类
- 统一返回结果封装(Result
- 第三方工具封装:
- Redis操作模板
- 文件上传工具
- 短信发送客户端
这种设计使得业务模块可以专注于核心逻辑,无需重复实现基础功能。例如,所有Controller都可以直接使用Result.success(data)返回统一格式的响应。
1.3 sky-pojo模块设计原理
sky-pojo模块集中管理所有数据模型,按照DDD(领域驱动设计)原则,主要包含三类对象:
1.3.1 DTO(Data Transfer Object)
DTO用于前后端数据传输,典型特征:
- 不包含业务逻辑
- 字段命名遵循前端习惯(如userId而非user_id)
- 包含数据校验注解(如@NotBlank)
示例中的CategoryDTO就是典型应用:
java复制public class CategoryDTO implements Serializable {
private Long id; // 使用包装类型以支持null
private Integer type; // 1-菜品分类 2-套餐分类
@NotBlank
private String name;
@Min(0)
private Integer sort;
}
1.3.2 Entity(实体类)
与数据库表直接映射的实体:
- 类名与表名对应(如User -> user表)
- 使用JPA或MyBatis注解配置映射关系
- 包含数据库特有字段(create_time等)
1.3.3 VO(View Object)
为前端展示定制的数据结构:
- 可能组合多个Entity字段
- 包含格式化后的数据(如日期字符串)
- 避免暴露敏感字段
1.4 为什么需要实现Serializable接口
所有数据对象实现Serializable是Java企业开发的通用实践,主要原因包括:
- 分布式缓存需求:Redis等缓存系统需要序列化对象
- RPC通信:Dubbo等框架依赖对象序列化
- Session共享:集群环境下Session序列化存储
- 消息队列:Kafka等消息中间件传输对象
JDK序列化机制通过ObjectOutputStream实现,其核心逻辑确实会检查Serializable接口:
java复制// 伪代码展示序列化过程
public void serialize(Object obj) throws IOException {
if (!(obj instanceof Serializable)) {
throw new NotSerializableException();
}
// 实际序列化逻辑...
}
实际项目中建议考虑使用JSON序列化替代JDK原生序列化,可减少存储空间并提高兼容性
2. 业务模块架构与分层设计
2.1 sky-server模块结构
作为核心业务模块,sky-server采用经典的三层架构:
code复制sky-server
├── controller # 控制层
├── service # 业务层
│ ├── impl # 实现类
├── mapper # 数据访问层
└── config # 配置类
2.2 分层架构的优势与实践
2.2.1 Controller层职责
- 接口定义(@RequestMapping)
- 参数校验(@Valid)
- 权限控制(@PreAuthorize)
- 日志记录
- 异常捕获
典型实现示例:
java复制@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@PostMapping
public Result addCategory(@RequestBody @Valid CategoryDTO dto) {
categoryService.addCategory(dto);
return Result.success();
}
}
2.2.2 Service层最佳实践
业务逻辑层的核心要点:
- 使用接口+实现类方式(便于扩展)
- 方法粒度要适中(避免"上帝服务")
- 事务控制(@Transactional)
- 异常处理(抛出自定义业务异常)
java复制public interface CategoryService {
void addCategory(CategoryDTO dto);
}
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Transactional
@Override
public void addCategory(CategoryDTO dto) {
// 业务校验
if (categoryMapper.existsByName(dto.getName())) {
throw new BusinessException("分类已存在");
}
// DTO转Entity
Category category = new Category();
BeanUtils.copyProperties(dto, category);
// 持久化
categoryMapper.insert(category);
}
}
2.2.3 Mapper层实现方式
数据访问层常见实现方案:
- MyBatis XML映射:传统方式,SQL与Java代码分离
- MyBatis注解:简单CRUD可直接用注解
- MyBatis-Plus:推荐方式,减少样板代码
MyBatis-Plus示例:
java复制public interface CategoryMapper extends BaseMapper<Category> {
@Select("SELECT COUNT(*) FROM category WHERE name = #{name}")
boolean existsByName(String name);
}
2.3 分层架构的扩展思考
现代Java开发中还可以考虑以下架构变体:
-
DDD分层:
- 用户接口层
- 应用层
- 领域层
- 基础设施层
-
六边形架构:
- 核心业务在内
- 适配器在外
- 依赖倒置
-
CQRS模式:
- 命令模型(写操作)
- 查询模型(读操作)
- 适合读写比高的系统
3. Java并发编程实战
3.1 线程模型与线程池
3.1.1 Java线程本质
Java线程是JVM对操作系统线程的封装:
- 在Linux上对应pthread
- 在Windows上对应Windows Thread
- 创建成本高(默认栈大小1MB)
因此实际开发中必须使用线程池管理线程资源。
3.1.2 ThreadPoolExecutor详解
核心参数配置示例:
java复制ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize (常驻线程)
8, // maximumPoolSize (最大线程)
30, // keepAliveTime (秒)
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 有界队列
new NamedThreadFactory("order-pool"), // 自定义线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
3.1.3 线程池工作流程
完整任务处理流程:
- 提交新任务
- 核心线程未满 → 创建新线程
- 核心线程已满 → 入队列
- 队列已满且线程未达最大值 → 创建临时线程
- 达到最大线程数且队列满 → 执行拒绝策略
关键点:队列容量和maxPoolSize需要平衡。队列太大导致响应延迟,maxPoolSize太大可能耗尽资源
3.1.4 生产环境配置建议
-
CPU密集型(如计算、加密):
- corePoolSize = CPU核心数
- maxPoolSize = CPU核心数 + 1
-
IO密集型(如网络请求、DB操作):
- 最佳线程数 = CPU核心数 * (1 + 平均等待时间/平均计算时间)
- 通常设置为CPU核心数的2-3倍
-
混合型任务:
- 拆分为不同线程池
- 或取中间值并监控调整
3.2 Tomcat线程池优化
Spring Boot内嵌Tomcat默认配置:
- maxThreads: 200
- acceptCount: 100 (等待队列)
高并发场景优化建议:
-
监控指标:
- 活跃线程数
- 队列积压
- 响应时间
-
防御措施:
properties复制server.tomcat.max-threads=500 server.tomcat.accept-count=50 server.connection-timeout=5s -
架构升级:
- 网关层限流(Nginx/Sentinel)
- 服务降级
- 异步非阻塞(WebFlux)
3.3 Java内存模型(JMM)
3.3.1 内存结构
JMM关键概念:
- 主内存:共享变量存储区域
- 工作内存:线程私有缓存
- happens-before:指令顺序保证
3.3.2 三大问题解决方案
-
可见性问题:
volatile关键字synchronized块final不可变对象
-
原子性问题:
AtomicInteger等原子类synchronized同步Lock显式锁
-
有序性问题:
volatile禁止重排序synchronized建立内存屏障
3.3.3 volatile深度解析
典型应用场景:
java复制public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
volatile在此解决了:1. 可见性问题 2. 指令重排序问题(防止返回未初始化完成的实例)
4. 生产环境问题与解决方案
4.1 线程池常见问题排查
4.1.1 线程泄漏
症状:线程数持续增长不释放
排查:
- 检查线程栈(jstack)
- 确认任务是否有阻塞操作(如无限循环)
- 检查是否忘记关闭资源(数据库连接等)
4.1.2 任务堆积
症状:队列积压,响应变慢
解决方案:
- 增加消费者线程
- 优化任务处理逻辑
- 引入背压机制
4.1.3 死锁问题
诊断方法:
bash复制jstack <pid> | grep -A 1 deadlock
预防措施:
- 避免嵌套锁
- 使用定时锁(tryLock)
- 统一加锁顺序
4.2 高并发场景优化
4.2.1 缓存策略
多级缓存架构:
- 本地缓存(Caffeine)
- 分布式缓存(Redis)
- 数据库缓存(MySQL Query Cache)
4.2.2 异步处理
典型场景:
- 日志记录
- 消息通知
- 耗时统计
实现方式:
java复制@Async("taskExecutor")
public void asyncProcess(Order order) {
// 异步处理逻辑
}
4.2.3 限流降级
常用工具:
- Sentinel
- Hystrix
- Resilience4j
配置示例:
java复制@SentinelResource(value = "createOrder",
blockHandler = "handleFlowLimit")
public Result createOrder(OrderDTO dto) {
// 业务逻辑
}
4.3 监控与调优
4.3.1 关键指标监控
-
JVM监控:
- GC次数/时间
- 堆内存使用
- 线程状态
-
业务监控:
- QPS
- 响应时间
- 错误率
4.3.2 性能分析工具
-
Arthas:在线诊断工具
- 监控方法调用
- 查看方法参数/返回值
- 热修复代码
-
JProfiler:内存分析
- 对象分配追踪
- CPU热点分析
- 内存泄漏检测
-
VisualVM:基础监控
- 线程dump
- 堆dump分析
- CPU采样
5. 架构演进与扩展思考
5.1 微服务化改造
当单体架构遇到瓶颈时,可考虑:
- 按业务拆分微服务
- 引入Spring Cloud生态:
- 服务注册发现(Nacos)
- 配置中心(Config)
- 服务网关(Gateway)
5.2 云原生适配
现代化部署方案:
- 容器化(Docker)
- 编排(Kubernetes)
- 服务网格(Istio)
5.3 领域驱动设计实践
复杂业务系统建议:
- 划分限界上下文
- 建立统一语言
- 使用CQRS模式
- 实现事件溯源
5.4 代码质量保障
持续交付流水线:
- 单元测试(JUnit5)
- 集成测试(TestContainers)
- 代码扫描(SonarQube)
- 自动化部署(Jenkins)
在实际开发中,架构设计需要根据团队规模、业务复杂度和演进阶段做出合适选择。对于初期项目,保持简单清晰的模块划分即可;当系统发展到一定规模,再逐步引入更复杂的架构模式。