三层架构

一、三层架构概述

三层架构(Three-Tier Architecture)是一种经典的软件设计模式,将应用程序划分为表现层(Controller)业务逻辑层(Service)数据访问层(DAO),通过职责分离实现高内聚、低耦合,提升代码的可维护性、扩展性和可测试性。


二、各层核心职责

1. 表现层(Controller)
  • 职责:接收客户端请求(HTTP/API)、参数校验、调用Service层处理业务、将结果转换为响应格式(JSON/HTML)返回。
  • 原则:不处理业务逻辑,仅做“请求转发器”和“响应组装器”。
2. 业务逻辑层(Service)
  • 职责:核心业务逻辑处理(如计算、规则判断、流程编排)、事务控制、调用DAO层进行数据交互。
  • 原则:业务逻辑集中于此,是连接Controller和DAO的桥梁。
3. 数据访问层(DAO/Repository)
  • 职责:直接与数据库/存储介质交互,执行CRUD(增删改查)操作,封装数据访问细节。
  • 原则:不涉及业务逻辑,仅专注“数据存取”。

三、交互流程

HTTP请求

调用业务方法

数据存取

SQL操作

返回数据

返回实体

业务处理

JSON响应

客户端

Controller

Service

DAO

数据库

四、优缺点分析

优点 缺点
职责单一,易于维护和扩展 简单项目会增加代码结构复杂度
各层可独立测试(如Mock Service测试Controller) 层级间依赖可能导致性能损耗(需合理设计)
便于团队协作(不同人负责不同层) 过度分层可能导致代码冗余

一、基于 Spring Boot + JPA 的三层架构代码示例

JPA(Java Persistence API) 是Java平台的标准ORM(对象关系映射)规范,用于简化Java对象与关系数据库之间的映射操作,让开发者通过面向对象的方式操作数据库,无需编写复杂的SQL。

1. 项目结构
src/main/java/com/example/demo
├── controller          # 表现层
│   └── UserController.java
├── service             # 业务逻辑层
│   ├── UserService.java
│   └── impl/UserServiceImpl.java
├── dao                 # 数据访问层
│   └── UserRepository.java
├── entity              # 数据库实体
│   └── User.java
└── dto                 # 数据传输对象
    ├── UserRequest.java
    └── UserResponse.java
2. 依赖配置(pom.xml)
<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- H2数据库(演示用) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
3. 数据库配置(application.properties)
# H2数据库配置
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
4. 代码实现
(1)Entity层:User.java(数据库实体)
package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "t_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String email;
    private Integer age;
}
(2)DTO层:请求/响应对象
// UserRequest.java(接收请求参数)
package com.example.demo.dto;

import lombok.Data;

@Data
public class UserRequest {
    private String username;
    private String email;
    private Integer age;
}

// UserResponse.java(返回响应数据)
package com.example.demo.dto;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class UserResponse {
    private Long id;
    private String username;
    private String email;
}
(3)DAO层:UserRepository.java(数据访问)
package com.example.demo.dao;

import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

// 继承JpaRepository,自动拥有CRUD方法
public interface UserRepository extends JpaRepository<User, Long> {
}
(4)Service层:业务逻辑处理
// UserService.java(接口)
package com.example.demo.service;

import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;

public interface UserService {
    UserResponse createUser(UserRequest request);
    UserResponse getUserById(Long id);
}

// UserServiceImpl.java(实现类)
package com.example.demo.service.impl;

import com.example.demo.dao.UserRepository;
import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    @Override
    @Transactional(rollbackFor = Exception.class) // 事务控制
    public UserResponse createUser(UserRequest request) {
        // 业务逻辑:比如校验用户名是否存在(示例简化)
        User user = new User();
        user.setUsername(request.getUsername());
        user.setEmail(request.getEmail());
        user.setAge(request.getAge());
        
        // 调用DAO层保存数据
        User savedUser = userRepository.save(user);
        
        // 转换为响应DTO返回
        return new UserResponse(savedUser.getId(), savedUser.getUsername(), savedUser.getEmail());
    }

    @Override
    public UserResponse getUserById(Long id) {
        // 调用DAO层查询数据
        User user = userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
        
        return new UserResponse(user.getId(), user.getUsername(), user.getEmail());
    }
}
(5)Controller层:请求处理
package com.example.demo.controller;

import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    // 创建用户:POST /api/users
    @PostMapping
    public ResponseEntity<UserResponse> createUser(@RequestBody UserRequest request) {
        UserResponse response = userService.createUser(request);
        return ResponseEntity.ok(response);
    }

    // 查询用户:GET /api/users/{id}
    @GetMapping("/{id}")
    public ResponseEntity<UserResponse> getUserById(@PathVariable Long id) {
        UserResponse response = userService.getUserById(id);
        return ResponseEntity.ok(response);
    }
}
5. 最佳实践
  1. 分层严格性:Controller不直接调用DAO,必须通过Service;Service只处理业务,不涉及HTTP请求/响应。
  2. DTO与Entity分离:用DTO(Data Transfer Object)传输数据,避免直接暴露数据库实体(Entity)。
  3. 事务管理:在Service层使用@Transactional控制事务,确保数据一致性。
  4. 异常处理:全局异常处理器(@ControllerAdvice)统一处理异常,返回友好响应。
  5. 单元测试:针对各层独立测试(如Mock DAO测试Service,Mock Service测试Controller)。

二、基于 Spring Boot + MyBatis-Plus 的三层架构代码示例

一、项目结构
src/main/java/com/example/demo
├── controller          # 表现层
│   └── UserController.java
├── service             # 业务逻辑层
│   ├── UserService.java
│   └── impl/UserServiceImpl.java
├── mapper              # 数据访问层(MyBatis 的 Mapper)
│   └── UserMapper.java
├── entity              # 数据库实体
│   └── User.java
├── dto                 # 数据传输对象
│   ├── UserRequest.java
│   └── UserResponse.java
├── common              # 公共模块
│   ├── Result.java     # 统一返回结果
│   └── BusinessException.java # 业务异常
└── config              # 配置类
    └── GlobalExceptionHandler.java # 全局异常处理器

二、核心依赖配置(pom.xml)
<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MyBatis-Plus(MyBatis 增强工具,简化 CRUD) -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>

    <!-- Lombok(简化实体类代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

三、数据库配置(application.yml)
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: your_password

# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 数据库下划线转驼峰
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL
  global-config:
    db-config:
      id-type: auto # 主键自增

四、数据库表初始化 SQL
CREATE DATABASE IF NOT EXISTS demo_db;
USE demo_db;

CREATE TABLE IF NOT EXISTS t_user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    email VARCHAR(100) COMMENT '邮箱',
    age INT COMMENT '年龄',
    status TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT '用户表';

五、代码实现
1. 公共模块(common)
(1)统一返回结果 Result.java
package com.example.demo.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private Integer code; // 状态码:200-成功,500-失败
    private String msg;   // 提示信息
    private T data;       // 返回数据

    // 成功静态方法
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    // 失败静态方法
    public static <T> Result<T> fail(String msg) {
        return new Result<>(500, msg, null);
    }
}
(2)业务异常 BusinessException.java
package com.example.demo.common;

import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
    private final String msg;

    public BusinessException(String msg) {
        super(msg);
        this.msg = msg;
    }
}
(3)全局异常处理器 GlobalExceptionHandler.java
package com.example.demo.config;

import com.example.demo.common.BusinessException;
import com.example.demo.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        return Result.fail(e.getMsg());
    }

    // 处理其他异常
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        e.printStackTrace();
        return Result.fail("系统异常,请联系管理员");
    }
}
2. 数据库实体(entity)
User.java
package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName("t_user") // 指定数据库表名
public class User {
    @TableId(type = IdType.AUTO) // 主键自增
    private Long id;
    private String username;
    private String email;
    private Integer age;
    private Integer status;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
3. 数据传输对象(dto)
(1)请求参数 UserRequest.java
package com.example.demo.dto;

import lombok.Data;

@Data
public class UserRequest {
    private String username;
    private String email;
    private Integer age;
}
(2)响应数据 UserResponse.java
package com.example.demo.dto;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
public class UserResponse {
    private Long id;
    private String username;
    private String email;
    private Integer age;
    private LocalDateTime createTime;
}
4. 数据访问层(mapper)
UserMapper.java
package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;

// 继承 MyBatis-Plus 的 BaseMapper,自动拥有 CRUD 方法
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
5. 业务逻辑层(service)
(1)接口 UserService.java
package com.example.demo.service;

import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;

public interface UserService {
    UserResponse createUser(UserRequest request);
    UserResponse getUserById(Long id);
    void updateUser(Long id, UserRequest request);
    void deleteUser(Long id);
}
(2)实现类 UserServiceImpl.java
package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.demo.common.BusinessException;
import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class) // 事务控制
    public UserResponse createUser(UserRequest request) {
        // 1. 业务校验:用户名是否已存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, request.getUsername());
        if (userMapper.selectCount(wrapper) > 0) {
            throw new BusinessException("用户名已存在");
        }

        // 2. 转换请求参数为实体
        User user = new User();
        BeanUtils.copyProperties(request, user);
        user.setStatus(1); // 默认状态正常

        // 3. 调用 Mapper 保存数据
        userMapper.insert(user);

        // 4. 转换为响应 DTO 返回
        return new UserResponse(user.getId(), user.getUsername(), user.getEmail(), user.getAge(), user.getCreateTime());
    }

    @Override
    public UserResponse getUserById(Long id) {
        // 1. 调用 Mapper 查询数据
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 2. 转换为响应 DTO 返回
        return new UserResponse(user.getId(), user.getUsername(), user.getEmail(), user.getAge(), user.getCreateTime());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(Long id, UserRequest request) {
        // 1. 校验用户是否存在
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 2. 更新字段
        BeanUtils.copyProperties(request, user);
        user.setId(id);

        // 3. 调用 Mapper 更新数据
        userMapper.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(Long id) {
        // 1. 校验用户是否存在
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 2. 调用 Mapper 删除数据
        userMapper.deleteById(id);
    }
}
6. 表现层(controller)
UserController.java
package com.example.demo.controller;

import com.example.demo.common.Result;
import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    // 创建用户:POST /api/users
    @PostMapping
    public Result<UserResponse> createUser(@RequestBody UserRequest request) {
        UserResponse response = userService.createUser(request);
        return Result.success(response);
    }

    // 查询用户:GET /api/users/{id}
    @GetMapping("/{id}")
    public Result<UserResponse> getUserById(@PathVariable Long id) {
        UserResponse response = userService.getUserById(id);
        return Result.success(response);
    }

    // 更新用户:PUT /api/users/{id}
    @PutMapping("/{id}")
    public Result<Void> updateUser(@PathVariable Long id, @RequestBody UserRequest request) {
        userService.updateUser(id, request);
        return Result.success(null);
    }

    // 删除用户:DELETE /api/users/{id}
    @DeleteMapping("/{id}")
    public Result<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return Result.success(null);
    }
}

六、启动类配置
package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描 Mapper 接口
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

七、关键说明(符合企业实战规范)
  1. 分层严格:Controller 仅处理请求/响应,Service 处理业务逻辑与事务,Mapper 仅做数据访问。
  2. 实体分离:Entity 对应数据库,DTO 用于前后端交互,避免敏感字段泄露。
  3. 事务控制@Transactional 加在 Service 方法上,确保数据一致性。
  4. 异常统一处理:全局异常处理器捕获业务异常和系统异常,返回统一格式。
  5. MyBatis-Plus 简化开发:继承 BaseMapper 自动拥有 CRUD 方法,无需编写基础 SQL。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐