springboot 项目的包结构设计(一)

在 Spring Boot 项目中,以下是几个包结构设计。

  1. 基于领域驱动设计和分层架构的包结构
  2. 基于功能分层而非按业务模块的包结构(下一篇介绍)

基于领域驱动设计和分层架构包结构

这个设计遵循了领域驱动设计(DDD)和分层架构的思想,同时保持了Spring Boot的简洁性。

txt 复制代码
src/main/java
└── com
    └── example
        └── app
            ├── Application.java                  # 启动类
            │
            ├── common                           # 公共模块
            │   ├── annotation                   # 自定义注解
            │   ├── config                       # 全局配置
            │   ├── constant                     # 常量定义
            │   ├── enums                        # 枚举类
            │   ├── exception                    # 异常处理
            │   │   ├── GlobalExceptionHandler.java
            │   │   └── BusinessException.java
            │   ├── model                        # 公共模型
            │   │   ├── page                     # 分页相关
            │   │   │   ├── PageRequest.java     # 基础分页请求类
            │   │   │   ├── PageResult.java      # 分页结果封装类
            │   │   │   └── PaginationUtils.java # 分页工具类
            │   │   ├── response                 # 响应封装
            │   │   │   ├── Result.java
            │   │   │   └── ErrorResponse.java
            │   │   └── validation               # 验证工具
            │   │       └── ValidatorUtils.java
            │   └── util                         # 工具类
            │       └── DateUtils.java
            │
            ├── modules                          # 业务模块
            │   ├── user                         # 用户模块
            │   │   ├── controller
            │   │   │   └── UserController.java
            │   │   ├── service
            │   │   │   ├── UserService.java
            │   │   │   └── impl
            │   │   │       └── UserServiceImpl.java
            │   │   ├── repository               # 数据访问层
            │   │   │   └── UserMapper.java
            │   │   ├── model                    # 模块内模型
            │   │   │   ├── dto                  # 数据传输对象
            │   │   │   │   ├── request
            │   │   │   │   │   ├── UserCreateRequest.java
            │   │   │   │   │   ├── UserUpdateRequest.java
            │   │   │   │   │   └── UserQueryRequest.java  # 继承PageRequest
            │   │   │   │   └── response
            │   │   │   │       └── UserResponse.java
            │   │   │   ├── entity               # 实体类
            │   │   │   │   └── UserEntity.java
            │   │   │   ├── vo                   # 视图对象
            │   │   │   │   └── UserVO.java
            │   │   │   └── query                # 查询条件
            │   │   │       └── UserQueryCondition.java
            │   │   └── converter                # 对象转换器
            │   │       └── UserConverter.java
            │   │
            │   └── product                     # 产品模块(类似结构)
            │       ├── controller
            │       ├── service
            │       ├── repository
            │       └── model
            │           ├── dto
            │           │   ├── request
            │           │   │   └── ProductQueryRequest.java
            │           │   └── response
            │           ├── entity
            │           └── query
            │
            ├── infrastructure                   # 基础设施层
            │   ├── persistence                  # 持久化相关
            │   │   └── mybatis                  # MyBatis配置
            │   │       ├── mapper               # Mapper XML文件
            │   │       └── config
            │   │           └── MyBatisConfig.java
            │   └── web                          # Web相关配置
            │       └── WebMvcConfig.java
            │
            └── security                         # 安全模块(可选)
                ├── config
                ├── filter
                └── service

关键包说明

  1. common (公共模块)
  • 存放整个项目通用的代码,避免重复。

  • 分页相关的类(PageRequest, PageResult)放在 common.model.dto 中。

  1. moduleX (业务模块)
  • 按功能模块划分,每个模块内聚自己的功能。

  • 模块内部再按分层(controller/service/mapper)组织。

  1. model 子包
  • entity : 数据库实体类(如 User

  • dto : 数据传输对象,用于前后端交互(如 UserDTO 用于创建/更新用户)

  • query : 查询条件封装(如 UserQuery,包含各种查询条件)

  • vo: 视图对象,专门为前端展示定制的模型(可能组合多个实体)

  1. infrastructure 包(基础设施)
  • persistence/mybatis:MyBatis 相关配置

    • mapper:XML 映射文件
    • config:MyBatis 配置类

包结构设计原则

  1. 模块化:按业务领域划分模块,而非技术层次

  2. 分层明确:controller -> service -> repository 清晰调用链

  3. 领域驱动:每个模块包含自己的模型、服务、仓储

  4. 高内聚低耦合

    • 模块间通过接口交互
    • 公共组件放在 common 包
  5. 可扩展性

    • 新增业务只需添加新模块
    • 不影响现有结构
  6. 关注点分离

    • 请求/响应 DTO 与实体分离
    • 业务逻辑与基础设施分离

这种包结构设计结合了领域驱动设计(DDD)和分层架构的优点,特别适合中大型 Spring Boot 项目。它提供了清晰的代码组织方式,良好的可维护性和可扩展性。

适配 MyBatis Dynamic SQL 的包结构最佳实践

如果要使用MyBatis Generator生成MyBatis Dynamic SQL代码,我们需要对包结构做一些调整,因为MyBatis Dynamic SQL采用了一种不同的编程模型。

MyBatis Dynamic SQL的特点:

  1. 使用Java DSL构建动态SQL

  2. 不需要XML映射文件

  3. 生成的代码包括表对象、支持类以及Mapper接口

为了适配前面的包结构,我们需要:

  1. 将生成的表对象(Table)放在合适的包下

  2. 将生成的Mapper接口放在repository包下

  3. 将生成的支持类(如主键、字段等)放在专门的包下

MyBatis Dynamic SQL 是一个强大的动态 SQL 构建框架,当它与 MyBatis Generator 结合使用时,可以生成高度优化的动态 SQL 代码。以下是针对我们之前设计的包结构,适配 MyBatis Dynamic SQL 的最佳实践方案:

调整后的包结构

csharp 复制代码
src/main/java
└── com
    └── example
        └── app
            ├── modules
            │   ├── user
            │   │   ├── controller
            │   │   ├── service
            │   │   ├── repository
            │   │   │   ├── dynamic          # MyBatis Dynamic SQL 生成代码
            │   │   │   │   ├── UserMapper.java
            │   │   │   │   ├── UserTable.java
            │   │   │   │   ├── UserDynamicSqlSupport.java
            │   │   │   │   └── ...
            │   │   │   └── UserRepository.java  # 自定义仓库接口
            │   │   ├── model
            │   │   │   ├── entity
            │   │   │   │   └── UserEntity.java  # MBG 生成的实体类
            │   │   │   ├── dto
            │   │   │   │   ├── request
            │   │   │   │   │   └── UserQueryRequest.java
            │   │   │   │   └── response
            │   │   │   └── query
            │   │   │       └── UserQueryCondition.java
            │   │   └── converter
            │   └── product
            │       └── ... # 类似结构
            └── infrastructure
                └── persistence
                    └── mybatis
                        ├── dynamic          # MyBatis Dynamic SQL 基础配置
                        │   ├── config
                        │   │   └── MyBatisDynamicConfig.java
                        │   └── support
                        │       └── BaseRepository.java
                        └── mapper           # XML 映射文件(如果需要)

MyBatis Generator 配置 (generatorConfig.xml)

xml 复制代码
<context id="MyBatis3DynamicSql" targetRuntime="MyBatis3DynamicSql">
    <!-- 数据库驱动 -->
    <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                    connectionURL="jdbc:mysql://localhost:3306/your_db"
                    userId="root"
                    password="password">
    </jdbcConnection>

    <!-- 实体类生成位置 -->
    <javaModelGenerator 
        targetPackage="com.example.app.modules.{module}.model.entity"
        targetProject="src/main/java">
        <property name="enableSubPackages" value="true"/>
        <property name="trimStrings" value="true"/>
    </javaModelGenerator>

    <!-- Dynamic SQL 支持类生成位置 -->
    <javaClientGenerator 
        type="ANNOTATEDMAPPER"
        targetPackage="com.example.app.modules.{module}.repository.dynamic"
        targetProject="src/main/java">
        <property name="enableSubPackages" value="true"/>
        <property name="rootInterface" value="com.example.app.infrastructure.persistence.mybatis.dynamic.support.BaseMapper"/>
    </javaClientGenerator>

    <!-- 表配置 -->
    <table tableName="user" 
           domainObjectName="UserEntity"
           mapperName="UserMapper">
        <generatedKey column="id" sqlStatement="MySql" identity="true"/>
    </table>
    
    <!-- 模块识别插件 -->
    <plugin type="com.example.app.infrastructure.persistence.mybatis.generator.ModuleAwareGeneratorPlugin"/>
</context>

基础接口和配置

1. 基础 Mapper 接口 (BaseMapper.java)

java 复制代码
// src/main/java/com/example/app/infrastructure/persistence/mybatis/dynamic/support/BaseMapper.java
public interface BaseMapper<T> {
    long count(CountDSLCompleter completer);
    
    int delete(DeleteDSLCompleter completer);
    
    int insert(T record);
    
    int insertMultiple(Collection<T> records);
    
    T selectOne(SelectDSLCompleter completer);
    
    List<T> select(SelectDSLCompleter completer);
    
    List<T> selectDistinct(SelectDSLCompleter completer);
    
    int update(UpdateDSLCompleter completer);
}

2. MyBatis Dynamic SQL 配置类

java 复制代码
// src/main/java/com/example/app/infrastructure/persistence/mybatis/dynamic/config/MyBatisDynamicConfig.java
@Configuration
public class MyBatisDynamicConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage("com.example.app.modules.*.model.entity");
        
        // 配置 MyBatis Dynamic SQL
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*.xml")
        );
        
        return sessionFactory.getObject();
    }
    
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    
    @Bean
    public MapperFactoryBean<BaseMapper> baseMapper(SqlSessionTemplate sqlSessionTemplate) {
        MapperFactoryBean<BaseMapper> factory = new MapperFactoryBean<>(BaseMapper.class);
        factory.setSqlSessionTemplate(sqlSessionTemplate);
        return factory;
    }
}

自定义 Repository 实现

1. 自定义 Repository 接口

java 复制代码
// src/main/java/com/example/app/modules/user/repository/UserRepository.java
public interface UserRepository {
    
    PageResult<UserEntity> findByCondition(
        UserQueryCondition condition, 
        PageRequest pageRequest
    );
    
    Optional<UserEntity> findByUsername(String username);
    
    // 其他自定义方法
}

2. Repository 实现类 (使用 Dynamic SQL)

java 复制代码
// src/main/java/com/example/app/modules/user/repository/impl/UserRepositoryImpl.java
@Repository
public class UserRepositoryImpl implements UserRepository {
    
    private final UserMapper userMapper;
    private final UserTable userTable = UserTable.user;
    
    @Autowired
    public UserRepositoryImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    
    @Override
    public PageResult<UserEntity> findByCondition(
        UserQueryCondition condition, 
        PageRequest pageRequest
    ) {
        // 构建动态查询
        SelectDSLCompleter completer = dsl -> dsl.where()
            .and(userTable.username, isLikeWhenPresent(condition.getUsername()))
            .and(userTable.status, isEqualToWhenPresent(condition.getStatus()))
            .orderBy(buildOrderSpecifier(pageRequest));
        
        // 查询总数
        long total = userMapper.count(completer);
        
        // 分页查询
        List<UserEntity> data = userMapper.select(completer, 
            c -> c.limit(pageRequest.getSize()).offset(pageRequest.getOffset()));
        
        return new PageResult<>(pageRequest, total, data);
    }
    
    private OrderByDSL buildOrderSpecifier(PageRequest pageRequest) {
        OrderByDSL orderBy = new OrderByDSL();
        if (pageRequest.getSort() != null) {
            BasicColumn column = getColumnForSort(pageRequest.getSort());
            if (column != null) {
                if ("desc".equalsIgnoreCase(pageRequest.getOrder())) {
                    orderBy.orderBy(column.descending());
                } else {
                    orderBy.orderBy(column);
                }
            }
        }
        return orderBy;
    }
    
    private BasicColumn getColumnForSort(String sortField) {
        // 映射排序字段到表列
        switch (sortField) {
            case "username": return userTable.username;
            case "createdAt": return userTable.createdAt;
            default: return null;
        }
    }
    
    @Override
    public Optional<UserEntity> findByUsername(String username) {
        return userMapper.selectOne(c -> c.where(userTable.username, isEqualTo(username)));
    }
}

Service 层适配

java 复制代码
// src/main/java/com/example/app/modules/user/service/impl/UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
    
    private final UserRepository userRepository;
    private final UserConverter userConverter;
    
    @Autowired
    public UserServiceImpl(UserRepository userRepository, UserConverter userConverter) {
        this.userRepository = userRepository;
        this.userConverter = userConverter;
    }
    
    @Override
    public PageResult<UserResponse> getUsers(UserQueryRequest request) {
        // 参数校验
        request.validate();
        
        // 转换查询条件
        UserQueryCondition condition = userConverter.toCondition(request);
        
        // 执行查询
        PageResult<UserEntity> entityResult = userRepository.findByCondition(
            condition, 
            request
        );
        
        // 转换为响应DTO
        return convertToResponse(entityResult);
    }
    
    private PageResult<UserResponse> convertToResponse(PageResult<UserEntity> entityResult) {
        List<UserResponse> responses = entityResult.getData().stream()
            .map(userConverter::toResponse)
            .collect(Collectors.toList());
            
        return new PageResult<>(
            entityResult.getPage(),
            entityResult.getSize(),
            entityResult.getTotal(),
            responses
        );
    }
}

分页查询优化

1. 分页参数构建器

java 复制代码
// src/main/java/com/example/app/common/model/page/PageDSLBuilder.java
public class PageDSLBuilder {

    public static <T> SelectDSLCompleter applyPagination(
        PageRequest pageRequest, 
        SelectDSLCompleter baseCompleter
    ) {
        return dsl -> {
            baseCompleter.accept(dsl);
            
            // 应用分页
            dsl.limit(pageRequest.getSize())
               .offset(pageRequest.getOffset());
            
            // 应用排序
            if (pageRequest.getSort() != null) {
                applySorting(dsl, pageRequest);
            }
        };
    }
    
    private static void applySorting(SelectDSL<?> dsl, PageRequest pageRequest) {
        // 根据请求构建排序逻辑
        // 实际实现中需要映射排序字段到表列
        if ("username".equals(pageRequest.getSort())) {
            if ("desc".equalsIgnoreCase(pageRequest.getOrder())) {
                dsl.orderBy(UserTable.user.username.descending());
            } else {
                dsl.orderBy(UserTable.user.username);
            }
        }
        // 添加更多字段...
    }
}

2. 在 Repository 中使用

java 复制代码
@Override
public PageResult<UserEntity> findByCondition(
    UserQueryCondition condition, 
    PageRequest pageRequest
) {
    // 基础查询条件
    SelectDSLCompleter baseCompleter = dsl -> dsl.where()
        .and(userTable.username, isLikeWhenPresent(condition.getUsername()))
        .and(userTable.status, isEqualToWhenPresent(condition.getStatus()));
    
    // 查询总数
    long total = userMapper.count(baseCompleter);
    
    // 应用分页和排序
    SelectDSLCompleter paginatedCompleter = 
        PageDSLBuilder.applyPagination(pageRequest, baseCompleter);
    
    // 执行分页查询
    List<UserEntity> data = userMapper.select(paginatedCompleter);
    
    return new PageResult<>(pageRequest, total, data);
}

高级特性:复杂查询构建

java 复制代码
@Override
public PageResult<UserEntity> searchUsers(UserQueryCondition condition, PageRequest pageRequest) {
    // 构建动态查询
    SelectDSLCompleter completer = dsl -> {
        dsl.where();
        
        // 基本条件
        if (condition.getUsername() != null) {
            dsl.and(userTable.username, isLike(condition.getUsername() + "%"));
        }
        
        // 状态条件
        if (condition.getStatus() != null) {
            dsl.and(userTable.status, isEqualTo(condition.getStatus()));
        }
        
        // 时间范围
        if (condition.getCreatedAtStart() != null || condition.getCreatedAtEnd() != null) {
            dsl.and(userTable.createdAt, 
                isBetween(condition.getCreatedAtStart())
                   .and(condition.getCreatedAtEnd()));
        }
        
        // 角色关联查询
        if (condition.getRoleIds() != null && !condition.getRoleIds().isEmpty()) {
            dsl.and(userTable.id, in(
                select(UserRoleTable.userRole.userId)
                .from(UserRoleTable.userRole)
                .where(UserRoleTable.userRole.roleId, isIn(condition.getRoleIds()))
            );
        }
    };
    
    // 查询总数
    long total = userMapper.count(completer);
    
    // 应用分页
    SelectDSLCompleter paginatedCompleter = 
        PageDSLBuilder.applyPagination(pageRequest, completer);
    
    // 执行查询
    List<UserEntity> data = userMapper.select(paginatedCompleter);
    
    return new PageResult<>(pageRequest, total, data);
}

MyBatis Generator 插件增强

模块感知插件

java 复制代码
// src/main/java/com/example/app/infrastructure/persistence/mybatis/generator/ModuleAwareGeneratorPlugin.java
public class ModuleAwareGeneratorPlugin extends PluginAdapter {

    @Override
    public void initialized(IntrospectedTable table) {
        String tableName = table.getFullyQualifiedTable().getIntrospectedTableName();
        String module = determineModule(tableName);
        
        // 设置实体类包路径
        String entityPackage = table.getContext().getJavaModelGeneratorConfiguration().getTargetPackage();
        entityPackage = entityPackage.replace("{module}", module);
        table.setBaseRecordType(entityPackage + "." + table.getBaseRecordType());
        
        // 设置Mapper包路径
        String mapperPackage = table.getContext().getJavaClientGeneratorConfiguration().getTargetPackage();
        mapperPackage = mapperPackage.replace("{module}", module);
        table.setMyBatis3JavaMapperType(mapperPackage + "." + table.getMyBatis3JavaMapperType());
        
        // 设置动态SQL支持类名称
        String supportClass = table.getFullyQualifiedTable().getDomainObjectName() + "DynamicSqlSupport";
        table.setDynamicSqlSupportType(mapperPackage + "." + supportClass);
    }
    
    private String determineModule(String tableName) {
        if (tableName.startsWith("user_")) return "user";
        if (tableName.startsWith("product_")) return "product";
        return "common";
    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
}

生成 Lombok 注解的实体类

java 复制代码
// src/main/java/com/example/app/infrastructure/persistence/mybatis/generator/LombokEntityGenerator.java
public class LombokEntityGenerator extends DefaultJavaModelGenerator {

    @Override
    public List<CompilationUnit> getCompilationUnits() {
        List<CompilationUnit> units = super.getCompilationUnits();
        
        units.stream()
            .filter(unit -> unit instanceof TopLevelClass)
            .map(unit -> (TopLevelClass) unit)
            .forEach(this::addLombokAnnotations);
        
        return units;
    }
    
    private void addLombokAnnotations(TopLevelClass topLevelClass) {
        topLevelClass.addImportedType("lombok.Getter");
        topLevelClass.addImportedType("lombok.Setter");
        topLevelClass.addImportedType("lombok.NoArgsConstructor");
        topLevelClass.addImportedType("lombok.AllArgsConstructor");
        topLevelClass.addImportedType("lombok.Builder");
        
        topLevelClass.addAnnotation("@Getter");
        topLevelClass.addAnnotation("@Setter");
        topLevelClass.addAnnotation("@NoArgsConstructor");
        topLevelClass.addAnnotation("@AllArgsConstructor");
        topLevelClass.addAnnotation("@Builder");
        
        // 移除生成的getter/setter
        topLevelClass.getMethods().clear();
    }
}

最佳实践总结

  1. 分层清晰

    • 生成的 Dynamic SQL 代码放在 repository.dynamic
    • 自定义 Repository 实现放在 repository
    • 实体类放在 model.entity
  2. 接口隔离

    • 使用自定义 Repository 接口隔离业务代码与生成代码
    • 业务层只依赖自定义 Repository 接口
  3. 动态查询

    • 利用 MyBatis Dynamic SQL 构建类型安全的动态查询
    • 封装分页逻辑到工具类中
  4. 模块化支持

    • 通过自定义插件实现按模块生成代码
    • 保持各模块代码的独立性
  5. 优化分页

    • 使用 DSL 构建器统一处理分页和排序
    • 避免在业务层直接处理分页细节
  6. 对象转换

    • 使用 Converter 将实体转换为 DTO
    • 保持业务层返回的是响应对象而非实体
  7. 类型安全

    • 利用 Dynamic SQL 的类型安全特性
    • 减少 SQL 注入风险
  8. 性能优化

    • 按需查询字段,避免 SELECT *
    • 使用 JOIN 替代 N+1 查询

生成代码示例

生成的实体类

java 复制代码
// src/main/java/com/example/app/modules/user/model/entity/UserEntity.java
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserEntity {
    private Long id;
    private String username;
    private String email;
    private Integer status;
    private Date createdAt;
    private Date updatedAt;
}

生成的 Dynamic SQL 支持类

java 复制代码
// src/main/java/com/example/app/modules/user/repository/dynamic/UserDynamicSqlSupport.java
public final class UserDynamicSqlSupport {
    public static final UserTable user = new UserTable();
    
    public static final SqlColumn<Long> id = user.id;
    public static final SqlColumn<String> username = user.username;
    public static final SqlColumn<String> email = user.email;
    public static final SqlColumn<Integer> status = user.status;
    public static final SqlColumn<Date> createdAt = user.createdAt;
    public static final SqlColumn<Date> updatedAt = user.updatedAt;
    
    public static final class UserTable extends SqlTable {
        public final SqlColumn<Long> id = column("id", JDBCType.BIGINT);
        public final SqlColumn<String> username = column("username", JDBCType.VARCHAR);
        public final SqlColumn<String> email = column("email", JDBCType.VARCHAR);
        public final SqlColumn<Integer> status = column("status", JDBCType.INTEGER);
        public final SqlColumn<Date> createdAt = column("created_at", JDBCType.TIMESTAMP);
        public final SqlColumn<Date> updatedAt = column("updated_at", JDBCType.TIMESTAMP);
        
        public UserTable() {
            super("user");
        }
    }
}

生成的 Mapper 接口

java 复制代码
// src/main/java/com/example/app/modules/user/repository/dynamic/UserMapper.java
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
    @SelectProvider(type = SqlProviderAdapter.class, method = "select")
    long count(CountDSLCompleter completer);
    
    @DeleteProvider(type = SqlProviderAdapter.class, method = "delete")
    int delete(DeleteDSLCompleter completer);
    
    @InsertProvider(type = SqlProviderAdapter.class, method = "insert")
    int insert(UserEntity record);
    
    // 其他基础方法...
}

执行生成命令

bash 复制代码
mvn mybatis-generator:generate

项目启动配置

java 复制代码
// src/main/java/com/example/app/Application.java
@SpringBootApplication
@MapperScan({
    "com.example.app.modules.*.repository.dynamic",
    "com.example.app.infrastructure.persistence.mybatis.dynamic.support"
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这种设计完美适配了 MyBatis Dynamic SQL,同时保持了项目结构的清晰和一致性。它结合了自动生成代码的高效性和自定义代码的灵活性,特别适合需要复杂动态查询的中大型项目。

分页查询流程

sequenceDiagram participant C as Client participant Controller participant Service participant Repository participant DB as Database C->>Controller: GET /users?page=2&size=5&name=Alice Controller->>Service: UserQueryRequest Service->>Repository: countUsers(condition) Repository->>DB: SELECT COUNT(*) DB-->>Repository: 48 Service->>Repository: selectUsers(condition, offset=5, size=5) Repository->>DB: SELECT * LIMIT 5,5 DB-->>Repository: List Service->>Service: convert to PageResult Service-->>Controller: PageResult Controller-->>C: 200 OK + JSON

最佳实践建议

  1. 严格分层
  • Controller 调用 Service,Service 调用 Mapper。

  • 避免跨层调用(如 Controller 直接调用 Mapper)。

  1. 使用 DTO 和 VO 分离模型
  • 数据库实体(Entity)只在数据访问层使用。

  • 服务层接收 DTO,返回 VO 或 DTO。

  • 避免直接暴露实体给前端。

  1. 模块独立性
  • 模块之间通过接口交互,避免直接依赖内部实现。

  • 每个模块有自己的模型,避免跨模块使用模型(需要时通过转换)。

  1. 统一响应格式
  • common.web 包中定义统一响应体(如 Result<T>)。
java 复制代码
public class Result<T> {

private int code;

private String message;

private T data;

}
  1. 全局异常处理
  • common.exception 包中实现 @ControllerAdvice 全局异常处理器。
  1. 配置集中管理
  • 所有配置类放在 common.config 包中。

  • 使用 @ConfigurationProperties 绑定配置参数。

相关推荐
JH30732 小时前
Java Stream API 在企业开发中的实战心得:高效、优雅的数据处理
java·开发语言·oracle
九月十九4 小时前
java使用aspose读取word里的图片
java·word
一 乐5 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
爱记录的小磊5 小时前
java-selenium自动化快速入门
java·selenium·自动化
鹏码纵横5 小时前
已解决:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 异常的正确解决方法,亲测有效!!!
java·python·mysql
weixin_985432115 小时前
Spring Boot 中的 @ConditionalOnBean 注解详解
java·spring boot·后端
Mr Aokey5 小时前
Java UDP套接字编程:高效实时通信的实战应用与核心类解析
java·java-ee
冬天vs不冷5 小时前
Java分层开发必知:PO、BO、DTO、VO、POJO概念详解
java·开发语言
hong_zc6 小时前
Java 文件操作与IO流
java·文件操作·io 流