MVC 分层架构
MVC 是一种软件架构模式,将应用程序分为三个核心部分,实现关注点分离。
一、MVC 基本概念
1. 三层结构
┌─────────────────────────────────────────────────────────┐
│ View (视图层) │
│ 负责展示数据和用户交互 │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Controller (控制层) │
│ 接收请求,调用业务逻辑,返回响应 │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Model (模型层) │
│ 业务逻辑和数据存取 │
└─────────────────────────────────────────────────────────┘2. 各层职责
| 层次 | 职责 | 示例 |
|---|---|---|
| View | 展示数据、用户交互 | JSP、Thymeleaf、Vue |
| Controller | 请求分发、参数校验 | Spring MVC Controller |
| Model | 业务逻辑、数据访问 | Service、Repository |
二、Java Web 分层架构
1. 经典四层架构
┌─────────────────────────────────────────────────────────┐
│ 表现层 (Web) │
│ Controller、DTO、VO │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 业务层 (Service) │
│ Service、BO、业务逻辑 │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 数据访问层 (DAO) │
│ Repository/Mapper、DO │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 数据库 (DB) │
│ MySQL、PostgreSQL │
└─────────────────────────────────────────────────────────┘2. 各层实现
Controller 层:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public Result<UserVO> create(@RequestBody @Valid UserDTO dto) {
UserVO user = userService.create(dto);
return Result.success(user);
}
@GetMapping("/{id}")
public Result<UserVO> getById(@PathVariable Long id) {
UserVO user = userService.getById(id);
return Result.success(user);
}
@PutMapping("/{id}")
public Result<UserVO> update(@PathVariable Long id, @RequestBody @Valid UserDTO dto) {
UserVO user = userService.update(id, dto);
return Result.success(user);
}
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Long id) {
userService.delete(id);
return Result.success();
}
}Service 层:
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserVO create(UserDTO dto) {
// 检查用户名是否存在
if (userRepository.existsByUsername(dto.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 创建用户
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setEmail(dto.getEmail());
userRepository.save(user);
return toVO(user);
}
@Override
@Transactional(readOnly = true)
public UserVO getById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("用户不存在"));
return toVO(user);
}
private UserVO toVO(User user) {
UserVO vo = new UserVO();
vo.setId(user.getId());
vo.setUsername(user.getUsername());
vo.setEmail(user.getEmail());
return vo;
}
}Repository 层:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUsername(String username);
Optional<User> findByUsername(String username);
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
}三、数据对象分类
1. 各类对象职责
| 对象 | 全称 | 位置 | 职责 |
|---|---|---|---|
| DTO | Data Transfer Object | Controller 层 | 接收请求参数 |
| VO | View Object | Controller 层 | 返回响应数据 |
| BO | Business Object | Service 层 | 业务逻辑对象 |
| DO | Data Object | DAO 层 | 数据库映射 |
| PO | Persistent Object | DAO 层 | 持久化对象 |
2. 对象转换
// DTO → BO
public UserBO toBO(UserDTO dto) {
UserBO bo = new UserBO();
bo.setUsername(dto.getUsername());
bo.setEmail(dto.getEmail());
return bo;
}
// BO → DO
public UserDO toDO(UserBO bo) {
UserDO user = new UserDO();
user.setUsername(bo.getUsername());
user.setEmail(bo.getEmail());
return user;
}
// DO → VO
public UserVO toVO(UserDO user) {
UserVO vo = new UserVO();
vo.setId(user.getId());
vo.setUsername(user.getUsername());
vo.setEmail(user.getEmail());
return vo;
}3. 使用 MapStruct
@Mapper(componentModel = "spring")
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(target = "id", ignore = true)
User toEntity(UserDTO dto);
UserVO toVO(User user);
@Mapping(target = "password", ignore = true)
UserVO toVOWithoutPassword(User user);
}四、分层架构的优势
1. 关注点分离
每层只关注自己的职责:
- Controller:参数校验、异常处理、响应封装
- Service:业务逻辑、事务管理
- Repository:数据访问、SQL 优化2. 易于测试
// Service 层单元测试
@SpringBootTest
class UserServiceTest {
@MockBean
private UserRepository userRepository;
@Autowired
private UserService userService;
@Test
void testCreate() {
// Given
UserDTO dto = new UserDTO();
dto.setUsername("test");
when(userRepository.existsByUsername("test")).thenReturn(false);
when(userRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
// When
UserVO result = userService.create(dto);
// Then
assertNotNull(result);
assertEquals("test", result.getUsername());
}
}3. 易于维护
修改数据库 → 只改 Repository 层
修改业务逻辑 → 只改 Service 层
修改 API 格式 → 只改 Controller 层五、分层架构演进
1. 传统三层
Controller → Service → DAO → Database2. DDD 分层
┌─────────────────────────────────────┐
│ Interface Layer │
│ (Controller/DTO) │
└─────────────────┬───────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Application Layer │
│ (Application Service) │
└─────────────────┬───────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Domain Layer │
│ (Entity/Value Object/Repository)│
└─────────────────┬───────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Infrastructure Layer │
│ (Persistence/External API) │
└─────────────────────────────────────┘3. 微服务架构
每个微服务内部保持分层:
API Gateway → Service A (分层) → Database A
→ Service B (分层) → Database B六、常见问题
1. 贫血模型 vs 充血模型
贫血模型:
// 只有 getter/setter,无业务逻辑
public class User {
private String username;
// getter/setter
}
// 业务逻辑在 Service 层
public class UserService {
public void changePassword(User user, String newPassword) {
user.setPassword(newPassword);
}
}充血模型:
// 包含业务逻辑
public class User {
private String username;
private String password;
public void changePassword(String oldPassword, String newPassword) {
if (!this.password.equals(oldPassword)) {
throw new BusinessException("原密码错误");
}
this.password = newPassword;
}
}2. 跨层调用
❌ 错误:Controller 直接调用 Repository
✅ 正确:Controller → Service → Repository3. 循环依赖
❌ Service A 依赖 Service B,Service B 依赖 Service A
✅ 提取公共逻辑到 Service C七、面试高频问题
Q1: 为什么要分层?
- 关注点分离
- 降低耦合
- 易于测试和维护
- 便于团队协作
Q2: 各层之间的依赖如何处理?
- 使用依赖注入
- 面向接口编程
- Spring 自动装配
Q3: DTO 和 VO 有什么区别?
| DTO | VO |
|---|---|
| 接收请求参数 | 返回响应数据 |
| 可包含校验注解 | 可脱敏处理 |
| 入参 | 出参 |
Q4: Service 层为什么要定义接口?
- 便于 Mock 测试
- 支持多实现
- 符合依赖倒置原则
- 但并非必须(简单项目可省略)
Q5: 事务应该放在哪一层?
- 放在 Service 层
- Controller 层不应该有事务
- 使用 @Transactional 注解
八、最佳实践
1. 分层规范
- 上层依赖下层,下层不依赖上层
- 同层之间避免相互调用
- 跨层调用通过接口2. 异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
return Result.fail(e.getMessage());
}
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.fail("系统异常");
}
}3. 统一响应格式
@Data
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> fail(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
}更新时间:2026年3月16日