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 包设计要点
- 职责单一:每个包有明确的职责范围
- 依赖方向:保持单向依赖,避免循环依赖
- 包间隔离:减少包之间的直接依赖
- 易于测试:包结构应该便于单元测试
8.2 代码组织建议
- 将相关的类放在同一个包中
- 使用子包来组织大量相关类
- 避免创建过深的包层次(通常不超过3-4层)
- 定期重构包结构以适应业务变化
8.3 银行项目建议结构
建议采用分层结构:
com.xie.bank/
├── controller/ # 新增:处理Web请求
├── service/ # 新增:业务逻辑层
├── dao/ # 现有:数据访问层
├── entity/ # 现有:实体类(建议从bean重命名)
├── util/ # 现有:工具类
├── exception/ # 新增:异常处理
└── config/ # 新增:配置类
这样的结构清晰明了,便于维护和扩展,也符合Java项目的通用规范。