Skip to content

代码结构

Spring Boot 在代码结构方面没有强制性要求,但遵循一些最佳实践可以让你的项目更清晰、更易维护。本文将详细介绍如何合理组织 Spring Boot 项目的代码结构。

TIP

如果你希望基于领域来强制执行结构,可以考虑使用 Spring Modulith

避免使用默认包

什么是默认包?

当一个类没有包含 package 声明时,它被认为是在"默认包"中。在 Spring Boot 应用中,强烈不建议使用默认包

为什么要避免默认包?

使用默认包会导致以下问题:

  • 对于使用 @ComponentScan@ConfigurationPropertiesScan@EntityScan@SpringBootApplication 注解的 Spring Boot 应用会产生特殊问题
  • 这些注解会读取每个 jar 包中的每个类,造成性能问题和不必要的扫描

推荐的包命名规范

IMPORTANT

建议遵循 Java 推荐的包命名约定,使用反向域名(例如:com.example.project)。

kotlin
package com.example.myapplication

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}
kotlin
// 没有 package 声明
@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

主应用类的位置

最佳实践:根包放置

建议将主应用类放置在其他类之上的根包中。这种做法有以下优势:

  1. 明确的搜索范围@SpringBootApplication 注解隐式定义了某些项目的基础"搜索包"
  2. JPA 实体扫描:如果编写 JPA 应用,@SpringBootApplication 注解类的包被用来搜索 @Entity 项目
  3. 组件扫描优化:使用根包可以让组件扫描仅应用于你的项目

典型的项目结构

以下是一个推荐的项目结构示例:

com
 +- example
     +- myapplication
         +- MyApplication.kt          // 主应用类
         |
         +- customer                  // 客户模块
         |   +- Customer.kt           // 实体类
         |   +- CustomerController.kt // 控制器
         |   +- CustomerService.kt    // 服务层
         |   +- CustomerRepository.kt // 数据访问层
         |
         +- order                     // 订单模块
             +- Order.kt              // 实体类
             +- OrderController.kt    // 控制器
             +- OrderService.kt       // 服务层
             +- OrderRepository.kt    // 数据访问层

业务场景示例

让我们通过一个电商系统的例子来演示合理的代码结构:

kotlin
package com.example.ecommerce

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class ECommerceApplication

fun main(args: Array<String>) {
    runApplication<ECommerceApplication>(*args)
}
kotlin
package com.example.ecommerce.customer

import jakarta.persistence.*

@Entity
@Table(name = "customers")
data class Customer(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    val name: String,
    val email: String,
    val phone: String
)
kotlin
package com.example.ecommerce.customer

import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/customers")
class CustomerController(
    private val customerService: CustomerService
) {

    @GetMapping
    fun getAllCustomers(): List<Customer> {
        return customerService.findAll()
    }

    @PostMapping
    fun createCustomer(@RequestBody customer: Customer): Customer {
        return customerService.save(customer)
    }
}

关于 @SpringBootApplication 注解

NOTE

如果你不想使用 @SpringBootApplication,可以使用它导入的 @EnableAutoConfiguration@ComponentScan 注解来代替。

kotlin
// 等价于 @SpringBootApplication
@EnableAutoConfiguration
@ComponentScan
@Configuration
class MyApplication

模块化结构设计

按功能模块组织

在大型应用中,按功能模块组织代码可以提高可维护性和可扩展性。使用场景:

  • 适用于中大型应用
  • 团队规模较大,代码量较多
  • 需要清晰的模块划分
  • 微服务架构

分层架构组织

如果项目较小,也可以按分层架构组织,使用场景:

  • 适用于小型应用或快速原型开发
  • 团队规模较小,代码量不大
  • 不需要复杂的模块化
com.example.ecommerce
 +- ECommerceApplication.kt
 |
 +- controller/           // 控制层
 |   +- CustomerController.kt
 |   +- OrderController.kt
 |
 +- service/             // 服务层
 |   +- CustomerService.kt
 |   +- OrderService.kt
 |
 +- repository/          // 数据访问层
 |   +- CustomerRepository.kt
 |   +- OrderRepository.kt
 |
 +- entity/              // 实体层
 |   +- Customer.kt
 |   +- Order.kt
 |
 +- config/              // 配置类
     +- DatabaseConfig.kt
     +- SecurityConfig.kt

实际项目示例

让我们看一个完整的小型电商项目结构:

kotlin
package com.example.ecommerce

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

/**
 * 电商系统主应用类
 * 位于根包,便于组件扫描和自动配置
 */
@SpringBootApplication
class ECommerceApplication

fun main(args: Array<String>) {
    runApplication<ECommerceApplication>(*args)
}
kotlin
package com.example.ecommerce.customer

import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class CustomerService(
    private val customerRepository: CustomerRepository
) {

    fun findAll(): List<Customer> = customerRepository.findAll()

    fun findById(id: Long): Customer? = customerRepository.findById(id).orElse(null)

    fun save(customer: Customer): Customer = customerRepository.save(customer)

    fun deleteById(id: Long) = customerRepository.deleteById(id)
}

最佳实践总结

IMPORTANT

代码结构最佳实践要点:

  1. 避免默认包:始终为类声明包名
  2. 使用反向域名:遵循 com.example.project 格式
  3. 主类放根包:将 @SpringBootApplication 类放在项目根包
  4. 模块化组织:按业务功能或技术分层组织代码
  5. 清晰的命名:包名和类名要体现其职责

TIP

良好的代码结构不仅有助于 Spring Boot 的自动配置和组件扫描,还能让团队协作更加高效,代码维护更加容易。

WARNING

注意避免包结构过深,一般 3-4 层包结构就足够了。过深的包结构会增加复杂性而不是简化代码组织。

通过遵循这些最佳实践,你的 Spring Boot 项目将具有清晰的结构,易于理解和维护,同时充分利用 Spring Boot 的自动配置特性。