Java项目包结构设计与功能划分详解

Java项目包结构设计与功能划分详解

1. 包结构设计原则

1.1 核心设计原则

  • 单一职责:每个包只负责一个特定的功能领域
  • 高内聚低耦合:相关类放在一起,减少包间依赖
  • 层次清晰:按照架构层次划分包结构
  • 易于导航:包名清晰表达功能意图

2. 标准包结构设计方案

2.1 基于分层架构的包结构(推荐)

复制代码
src/main/java/com/xie/bank/
├── controller/           # 控制层 - 处理HTTP请求
├── service/             # 业务逻辑层
│   ├── impl/           # 业务实现
│   └── dto/            # 数据传输对象
├── dao/                 # 数据访问层
│   ├── impl/           # DAO实现
│   └── entity/         # 实体类
├── util/               # 工具类
├── config/             # 配置类
├── exception/          # 异常类
├── constant/           # 常量定义
└── interceptor/        # 拦截器

2.2 基于功能模块的包结构

复制代码
src/main/java/com/xie/bank/
├── account/            # 账户模块
│   ├── controller/
│   ├── service/
│   ├── dao/
│   └── entity/
├── user/               # 用户模块
│   ├── controller/
│   ├── service/
│   ├── dao/
│   └── entity/
├── transaction/        # 交易模块
│   ├── controller/
│   ├── service/
│   ├── dao/
│   └── entity/
└── common/             # 公共模块
    ├── util/
    ├── config/
    ├── exception/
    └── constant/

3. 详细包功能说明

3.1 controller包 - 控制层

java 复制代码
package com.xie.bank.controller;

import com.xie.bank.service.AccountService;
import com.xie.bank.entity.Account;
import org.springframework.web.bind.annotation.*;

/**
 * 账户控制器
 * 职责:接收HTTP请求,调用Service,返回响应
 */
@RestController
@RequestMapping("/api/accounts")
public class AccountController {
    
    private final AccountService accountService;
    
    public AccountController(AccountService accountService) {
        this.accountService = accountService;
    }
    
    @PostMapping
    public ResponseEntity<Account> createAccount(@RequestBody Account account) {
        Account created = accountService.createAccount(account);
        return ResponseEntity.ok(created);
    }
    
    @GetMapping("/{accountNumber}")
    public ResponseEntity<Account> getAccount(@PathVariable String accountNumber) {
        Account account = accountService.getAccountByNumber(accountNumber);
        return ResponseEntity.ok(account);
    }
}

3.2 service包 - 业务逻辑层

java 复制代码
package com.xie.bank.service;

import com.xie.bank.entity.Account;

/**
 * 账户业务服务接口
 * 职责:定义业务操作契约
 */
public interface AccountService {
    Account createAccount(Account account);
    Account getAccountByNumber(String accountNumber);
    boolean transfer(String fromAccount, String toAccount, double amount);
}

package com.xie.bank.service.impl;

import com.xie.bank.service.AccountService;
import com.xie.bank.dao.AccountDao;
import com.xie.bank.entity.Account;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 账户业务服务实现
 * 职责:实现业务逻辑,协调多个DAO操作
 */
@Service
public class AccountServiceImpl implements AccountService {
    
    private final AccountDao accountDao;
    
    public AccountServiceImpl(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    @Override
    @Transactional
    public Account createAccount(Account account) {
        // 业务逻辑验证
        validateAccount(account);
        // 调用DAO
        accountDao.insert(account);
        return account;
    }
    
    private void validateAccount(Account account) {
        // 业务规则验证
    }
}

3.3 dao包 - 数据访问层

java 复制代码
package com.xie.bank.dao;

import com.xie.bank.entity.Account;
import java.util.List;

/**
 * 账户数据访问接口
 * 职责:定义数据操作契约
 */
public interface AccountDao {
    int insert(Account account);
    int update(Account account);
    Account selectByAccountNumber(String accountNumber);
    List<Account> selectAll();
}

package com.xie.bank.dao.impl;

import com.xie.bank.dao.AccountDao;
import com.xie.bank.entity.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 账户数据访问实现
 * 职责:实现具体的数据操作
 */
@Repository
public class AccountDaoImpl implements AccountDao {
    
    private final JdbcTemplate jdbcTemplate;
    
    public AccountDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    @Override
    public int insert(Account account) {
        String sql = "INSERT INTO account(account_number, balance) VALUES(?, ?)";
        return jdbcTemplate.update(sql, account.getAccountNumber(), account.getBalance());
    }
    
    @Override
    public Account selectByAccountNumber(String accountNumber) {
        String sql = "SELECT * FROM account WHERE account_number = ?";
        return jdbcTemplate.queryForObject(sql, 
            new BeanPropertyRowMapper<>(Account.class), accountNumber);
    }
}

3.4 entity包 - 实体类

java 复制代码
package com.xie.bank.entity;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 账户实体类
 * 职责:表示业务数据模型
 */
public class Account {
    private Long id;
    private String accountNumber;
    private BigDecimal balance;
    private LocalDateTime createTime;
    private Integer status;
    
    // 构造方法、getter、setter
    public Account() {}
    
    public Account(String accountNumber, BigDecimal balance) {
        this.accountNumber = accountNumber;
        this.balance = balance;
        this.createTime = LocalDateTime.now();
        this.status = 1; // 正常状态
    }
    
    // getter和setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getAccountNumber() { return accountNumber; }
    public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
    
    public BigDecimal getBalance() { return balance; }
    public void setBalance(BigDecimal balance) { this.balance = balance; }
    
    public LocalDateTime getCreateTime() { return createTime; }
    public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
    
    public Integer getStatus() { return status; }
    public void setStatus(Integer status) { this.status = status; }
}

3.5 dto包 - 数据传输对象

java 复制代码
package com.xie.bank.service.dto;

import java.math.BigDecimal;

/**
 * 账户创建请求DTO
 * 职责:在层间传输数据,避免暴露实体类细节
 */
public class AccountCreateRequest {
    private String accountNumber;
    private BigDecimal initialBalance;
    private Long customerId;
    
    // 构造方法、getter、setter
    public AccountCreateRequest() {}
    
    public AccountCreateRequest(String accountNumber, BigDecimal initialBalance, Long customerId) {
        this.accountNumber = accountNumber;
        this.initialBalance = initialBalance;
        this.customerId = customerId;
    }
    
    // getter和setter
    public String getAccountNumber() { return accountNumber; }
    public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
    
    public BigDecimal getInitialBalance() { return initialBalance; }
    public void setInitialBalance(BigDecimal initialBalance) { this.initialBalance = initialBalance; }
    
    public Long getCustomerId() { return customerId; }
    public void setCustomerId(Long customerId) { this.customerId = customerId; }
}

3.6 util包 - 工具类

java 复制代码
package com.xie.bank.util;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 数据库工具类
 * 职责:提供通用的数据库操作工具方法
 */
public final class DBUtil {
    
    private DBUtil() {
        // 工具类,防止实例化
    }
    
    public static void closeQuietly(AutoCloseable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                // 安静关闭,不抛出异常
                System.err.println("关闭资源时发生错误: " + e.getMessage());
            }
        }
    }
    
    public static boolean isValidConnection(Connection conn) {
        if (conn == null) return false;
        try {
            return !conn.isClosed() && conn.isValid(2);
        } catch (SQLException e) {
            return false;
        }
    }
}

3.7 exception包 - 异常类

java 复制代码
package com.xie.bank.exception;

/**
 * 业务异常基类
 * 职责:统一的业务异常处理
 */
public class BusinessException extends RuntimeException {
    private final String errorCode;
    
    public BusinessException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    
    public BusinessException(String errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }
    
    public String getErrorCode() {
        return errorCode;
    }
}

package com.xie.bank.exception;

/**
 * 账户相关业务异常
 */
public class AccountException extends BusinessException {
    
    public AccountException(String errorCode, String message) {
        super(errorCode, message);
    }
    
    public AccountException(String errorCode, String message, Throwable cause) {
        super(errorCode, message, cause);
    }
}

3.8 config包 - 配置类

java 复制代码
package com.xie.bank.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * 数据库配置类
 * 职责:配置数据源和JDBC模板
 */
@Configuration
public class DatabaseConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bank");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        dataSource.setMaximumPoolSize(20);
        dataSource.setMinimumIdle(5);
        return dataSource;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

3.9 constant包 - 常量定义

java 复制代码
package com.xie.bank.constant;

/**
 * 账户状态常量
 * 职责:集中管理业务常量
 */
public final class AccountStatus {
    private AccountStatus() {}
    
    public static final int ACTIVE = 1;      // 正常状态
    public static final int FROZEN = 2;      // 冻结状态
    public static final int CLOSED = 0;      // 关闭状态
}

package com.xie.bank.constant;

/**
 * 错误代码常量
 */
public final class ErrorCode {
    private ErrorCode() {}
    
    public static final String ACCOUNT_NOT_FOUND = "ACCOUNT_001";
    public static final String INSUFFICIENT_BALANCE = "ACCOUNT_002";
    public static final String ACCOUNT_ALREADY_EXISTS = "ACCOUNT_003";
}

4. 包依赖关系规范

4.1 允许的依赖方向

复制代码
controller → service → dao → entity
                ↓
            dto, exception, constant

util, config ← 所有层都可以依赖

4.2 禁止的依赖关系

java 复制代码
// ❌ 错误:dao层依赖service层
package com.xie.bank.dao;
import com.xie.bank.service.AccountService; // 禁止!

// ❌ 错误:entity依赖dao
package com.xie.bank.entity;
import com.xie.bank.dao.AccountDao; // 禁止!

// ❌ 错误:util包依赖业务层
package com.xie.bank.util;
import com.xie.bank.service.AccountService; // 禁止!

5. 实际项目包结构示例

5.1 完整的银行系统包结构

复制代码
src/main/java/com/xie/bank/
├── BankApplication.java                 # 应用启动类
├── config/
│   ├── DatabaseConfig.java
│   ├── WebConfig.java
│   └── SecurityConfig.java
├── controller/
│   ├── AccountController.java
│   ├── UserController.java
│   └── TransactionController.java
├── service/
│   ├── AccountService.java
│   ├── UserService.java
│   ├── TransactionService.java
│   ├── impl/
│   │   ├── AccountServiceImpl.java
│   │   ├── UserServiceImpl.java
│   │   └── TransactionServiceImpl.java
│   └── dto/
│       ├── AccountCreateRequest.java
│       ├── TransferRequest.java
│       └── UserRegistrationRequest.java
├── dao/
│   ├── AccountDao.java
│   ├── UserDao.java
│   ├── TransactionDao.java
│   ├── impl/
│   │   ├── AccountDaoImpl.java
│   │   ├── UserDaoImpl.java
│   │   └── TransactionDaoImpl.java
│   └── entity/
│       ├── Account.java
│       ├── User.java
│       └── Transaction.java
├── util/
│   ├── DBUtil.java
│   ├── DateUtil.java
│   └── StringUtil.java
├── exception/
│   ├── BusinessException.java
│   ├── AccountException.java
│   └── GlobalExceptionHandler.java
├── constant/
│   ├── AccountStatus.java
│   ├── TransactionType.java
│   └── ErrorCode.java
└── interceptor/
    ├── AuthInterceptor.java
    └── LoggingInterceptor.java

6. 包命名规范

6.1 包名约定

  • 全部小写controller 而不是 Controller
  • 单数形式exception 而不是 exceptions
  • 有意义的名字 :避免使用 misc, common 等模糊名称
  • 反向域名com.xie.bank 确保唯一性

6.2 子包命名

java 复制代码
// 好的包名
com.xie.bank.service.impl      // 实现包
com.xie.bank.web.controller    // Web控制器
com.xie.bank.dao.repository    // 数据仓库

// 避免的包名
com.xie.bank.service.ServiceImpl    // 重复含义
com.xie.bank.etc                  // 不明确的名称
com.xie.bank.misc                 // 杂项,应该分解

7. 包结构调整策略

7.1 小型项目(推荐分层结构)

复制代码
com.xie.bank.
├── controller
├── service
├── dao
└── entity

7.2 中型项目(模块化分层)

复制代码
com.xie.bank.
├── account/
│   ├── controller
│   ├── service
│   └── dao
├── user/
│   ├── controller
│   ├── service
│   └── dao
└── common/
    ├── util
    ├── config
    └── exception

7.3 大型项目(领域驱动设计)

复制代码
com.xie.bank.
├── account/
│   ├── application/    # 应用服务
│   ├── domain/         # 领域模型
│   ├── infrastructure/ # 基础设施
│   └── interfaces/     # 接口适配器
├── user/
│   ├── application/
│   ├── domain/
│   ├── infrastructure/
│   └── interfaces/
└── shared/
    ├── kernel/         # 核心共享
    └── common/         # 通用组件

8. 最佳实践总结

8.1 包设计要点

  1. 职责单一:每个包有明确的职责范围
  2. 依赖方向:保持单向依赖,避免循环依赖
  3. 包间隔离:减少包之间的直接依赖
  4. 易于测试:包结构应该便于单元测试

8.2 代码组织建议

  • 将相关的类放在同一个包中
  • 使用子包来组织大量相关类
  • 避免创建过深的包层次(通常不超过3-4层)
  • 定期重构包结构以适应业务变化

8.3 银行项目建议结构

建议采用分层结构:

复制代码
com.xie.bank/
├── controller/     # 新增:处理Web请求
├── service/        # 新增:业务逻辑层
├── dao/           # 现有:数据访问层
├── entity/        # 现有:实体类(建议从bean重命名)
├── util/          # 现有:工具类
├── exception/     # 新增:异常处理
└── config/        # 新增:配置类

这样的结构清晰明了,便于维护和扩展,也符合Java项目的通用规范。

相关推荐
市民中心的蟋蟀2 小时前
第三章 钩入React 【上】
前端·react.js·架构
ss2732 小时前
手写MyBatis第92弹:SqlSource体系、SqlNode树与Trim标签实现原理全揭秘
java·开发语言
2501_915909062 小时前
iOS 抓包工具有哪些?实战对比、场景分工与开发者排查流程
android·开发语言·ios·小程序·uni-app·php·iphone
charlie1145141913 小时前
精读C++设计模式20 —— 结构型设计模式:桥接模式
开发语言·c++·学习·设计模式·桥接模式·c++23·概论
235163 小时前
【LeetCode】46. 全排列
java·数据结构·后端·算法·leetcode·职场和发展·深度优先
_extraordinary_3 小时前
Java Linux --- 基本命令,部署Java web程序到线上访问
java·linux·前端
heyCHEEMS3 小时前
最长连续序列 Java
java·开发语言·算法
MFine4 小时前
Rhythmix(流式数据规则表达式),一行就够了!
java·物联网·数据分析
waves浪游4 小时前
C++多态
开发语言·c++