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包 - 控制层
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;}@PostMappingpublic 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包 - 业务逻辑层
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@Transactionalpublic Account createAccount(Account account) {// 业务逻辑验证validateAccount(account);// 调用DAOaccountDao.insert(account);return account;}private void validateAccount(Account account) {// 业务规则验证}
}
3.3 dao包 - 数据访问层
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;}@Overridepublic int insert(Account account) {String sql = "INSERT INTO account(account_number, balance) VALUES(?, ?)";return jdbcTemplate.update(sql, account.getAccountNumber(), account.getBalance());}@Overridepublic Account selectByAccountNumber(String accountNumber) {String sql = "SELECT * FROM account WHERE account_number = ?";return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), accountNumber);}
}
3.4 entity包 - 实体类
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、setterpublic 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包 - 数据传输对象
package com.xie.bank.service.dto;import java.math.BigDecimal;/*** 账户创建请求DTO* 职责:在层间传输数据,避免暴露实体类细节*/
public class AccountCreateRequest {private String accountNumber;private BigDecimal initialBalance;private Long customerId;// 构造方法、getter、setterpublic AccountCreateRequest() {}public AccountCreateRequest(String accountNumber, BigDecimal initialBalance, Long customerId) {this.accountNumber = accountNumber;this.initialBalance = initialBalance;this.customerId = customerId;}// getter和setterpublic 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包 - 工具类
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包 - 异常类
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包 - 配置类
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 {@Beanpublic 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;}@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}
}
3.9 constant包 - 常量定义
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, constantutil, config ← 所有层都可以依赖
4.2 禁止的依赖关系
// ❌ 错误: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 子包命名
// 好的包名
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项目的通用规范。