1.三层架构
- 在单层架构中,数据访问,逻辑处理和请求响应均放在同一类中


- 三层架构

- 以下按照 实体类 → Controller 层 → Service 层 → Dao 层 的顺序重新整理代码,包含 @RestController 及详细注解,基于 Spring Boot 框架规范实现:
- 实体类(User.java)
java
import lombok.Data;
/**
* 用户实体类(POJO)
* 用于封装用户数据,作为各层之间的数据传输载体
*/
@Data // Lombok注解:自动生成getter、setter、toString、equals等方法,简化代码
public class User {
private Integer id; // 用户唯一标识ID
private String username; // 用户名(登录账号)
private String password; // 密码(实际开发中需加密存储,如BCrypt加密)
private String nickname; // 用户昵称(显示用)
}
- Controller 层(UserController.java)
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* 用户控制层
* 负责接收前端HTTP请求,调用Service层处理业务,返回JSON响应
* @RestController = @Controller + @ResponseBody:标识为控制器且所有方法返回JSON
*/
@RestController
@RequestMapping("/api/v1/users") // 接口统一前缀,用于版本控制和路径规划
public class UserController {
/**
* 依赖注入:自动从Spring容器中获取UserService实例
* 无需手动new,降低耦合度,便于测试和扩展
*/
@Autowired
private UserService userService;
/**
* 根据用户ID查询用户信息
* @param id 路径参数(用户ID),通过@PathVariable绑定
* @return 包含用户数据的响应实体(状态码+数据)
*/
@GetMapping("/{id}") // 处理GET请求,路径为 /api/v1/users/{id}
public ResponseEntity<User> getUserById(
@PathVariable Integer id, // 绑定URL路径中的{id}参数
@RequestHeader(required = false) String token // 可选:获取请求头中的token(用于鉴权)
) {
// 实际开发中可在此处添加前置校验(如token验证)
User user = userService.getUserById(id);
// ResponseEntity封装响应:200状态码 + 用户数据
return ResponseEntity.ok(user);
}
/**
* 新增用户
* @param user 请求体中的用户数据(JSON格式),通过@RequestBody绑定
* @return 新增后的用户数据(包含自动生成的ID)
*/
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 成功时响应201状态码(资源创建成功)
public User createUser(@RequestBody User user) { // 接收JSON请求体并转为User对象
return userService.addUser(user);
}
/**
* 全局异常处理:捕获参数非法异常
* 统一处理异常,避免返回默认错误页面,提升用户体验
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST) // 响应400状态码(参数错误)
public String handleIllegalArgument(IllegalArgumentException e) {
return "参数错误:" + e.getMessage();
}
/**
* 全局异常处理:捕获用户不存在异常
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) // 响应404状态码(资源不存在)
public String handleUserNotFound(RuntimeException e) {
return "错误:" + e.getMessage();
}
}
-
- Service 层
接口:UserService.java
- Service 层
java
/**
* 用户业务逻辑接口
* 定义用户相关的业务操作规范,隔离Controller层与数据访问层
*/
public interface UserService {
/**
* 根据ID查询用户
* @param id 用户ID
* @return 用户实体(若不存在则抛出异常)
*/
User getUserById(Integer id);
/**
* 新增用户
* @param user 待新增的用户数据(不含ID,由数据库自动生成)
* @return 新增后的用户数据(包含ID)
*/
User addUser(User user);
}
-
- 实现类:UserServiceImpl.java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 用户业务逻辑实现类
* 处理核心业务逻辑(如参数校验、事务控制等),调用Dao层操作数据
*/
@Service // Spring注解:标识为业务层组件,交由Spring容器管理
public class UserServiceImpl implements UserService {
/**
* 依赖注入:自动获取UserDao实例
*/
@Autowired
private UserDao userDao;
@Override
public User getUserById(Integer id) {
// 业务校验:参数合法性判断
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户ID必须为正整数");
}
// 调用Dao层查询数据
User user = userDao.selectById(id);
// 业务逻辑:用户不存在时抛出异常
if (user == null) {
throw new RuntimeException("用户不存在(ID:" + id + ")");
}
return user;
}
@Override
public User addUser(User user) {
// 业务校验:用户名不能为空
if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
// 调用Dao层插入数据
userDao.insert(user);
// 返回插入后的用户(包含自动生成的ID)
return user;
}
}
-
- Dao 层(数据访问层)-注解的方式
java
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* 用户数据访问接口
* 定义与数据库交互的方法,由MyBatis自动生成实现类
*/
@Mapper // MyBatis注解:标识为数据访问接口,SpringBoot会自动扫描并创建代理对象
public interface UserDao {
/**
* 根据ID查询用户
* @param id 用户ID
* @return 用户实体(若不存在则返回null)
*/
@Select("SELECT id, username, password, nickname FROM user WHERE id = #{id}")
User selectById(Integer id);
/**
* 新增用户
* @param user 待插入的用户数据(ID会由数据库自动生成)
*/
@Insert("INSERT INTO user (username, password, nickname) VALUES (#{username}, #{password}, #{nickname})")
void insert(User user);
}
-
- Dao 层(数据访问层) -接口-实现类的形式
接口:UserDao.java
- Dao 层(数据访问层) -接口-实现类的形式
java
import org.springframework.stereotype.Repository;
/**
* 用户数据访问接口
* 定义与用户相关的数据库操作规范(增删改查)
* 接口仅声明方法,具体实现由实现类完成,便于更换数据库访问方式(如JDBC/MyBatis)
*/
@Repository // Spring注解:标识为数据访问层组件,纳入容器管理(语义化注解,便于分层识别)
public interface UserDao {
/**
* 根据ID查询用户
* @param id 用户ID
* @return 匹配的用户实体,无数据则返回null
*/
User selectById(Integer id);
/**
* 新增用户
* @param user 待插入的用户数据(ID由数据库自动生成)
* @return 受影响的行数(1表示成功,0表示失败)
*/
int insert(User user);
/**
* 根据ID更新用户信息
* @param user 包含更新信息的用户实体(必须包含ID)
* @return 受影响的行数(1表示成功,0表示失败)
*/
int updateById(User user);
/**
* 根据ID删除用户
* @param id 用户ID
* @return 受影响的行数(1表示成功,0表示失败)
*/
int deleteById(Integer id);
}
-
- 实现类:(由 MyBatis 自动生成,无需手动编写)
MyBatis 会通过接口和 XML 映射文件动态生成实现类,无需开发者手动编写实现代码。核心是通过 XML 文件定义 SQL 与接口方法的映射关系。
- 实现类:(由 MyBatis 自动生成,无需手动编写)
java
Mapper XML 文件:UserDao.xml(放在 resources/mapper 目录下)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:必须与Dao接口的全类名一致,建立接口与XML的绑定关系
即:接口包名.接口名 = com.example.dao.UserDao
-->
<mapper namespace="com.example.dao.UserDao">
<!-- 定义结果集映射:数据库字段与实体类属性的映射关系(字段名与属性名不一致时必须配置) -->
<resultMap id="BaseResultMap" type="com.example.entity.User">
<id column="id" property="id"/> <!-- 主键字段映射 -->
<result column="username" property="username"/> <!-- 普通字段映射 -->
<result column="password" property="password"/>
<result column="nickname" property="nickname"/>
</resultMap>
<!--
select标签:对应查询操作
id:必须与接口中的方法名一致(selectById)
parameterType:参数类型(Integer)
resultMap:引用上面定义的结果集映射(BaseResultMap)
-->
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
SELECT id, username, password, nickname
FROM user
WHERE id = #{id} <!-- #{id}:参数占位符,MyBatis自动处理SQL注入 -->
</select>
<!-- insert标签:对应新增操作 -->
<insert id="insert" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">
<!--
useGeneratedKeys="true":开启自增主键获取
keyProperty="id":将数据库生成的主键值设置到User对象的id属性中
-->
INSERT INTO user (username, password, nickname)
VALUES (#{username}, #{password}, #{nickname})
</insert>
<!-- update标签:对应更新操作 -->
<update id="updateById" parameterType="com.example.entity.User">
UPDATE user
SET username = #{username},
password = #{password},
nickname = #{nickname}
WHERE id = #{id} <!-- 条件:根据ID更新 -->
</update>
<!-- delete标签:对应删除操作 -->
<delete id="deleteById" parameterType="java.lang.Integer">
DELETE FROM user
WHERE id = #{id}
</delete>
</mapper>



2.分层解耦

- 高内聚和低耦合

- 如何实现高内聚和低耦合?


2.1 分层解耦的实现 - "控制反转(IOC)" 和 "依赖注入(DI)"的核心概念,解释了如何通过这两个技术实现分层解耦 **。
- 一、代码层的 "耦合问题"(未用 IOC/DI 时)
先看右侧的 UserServiceImpl 类:
java
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl(); // 硬编码创建依赖
// ...
}
这里 UserServiceImpl 直接 new UserDaoImpl(),意味着Service 层和 Dao 层 "强绑定"------
如果要换 Dao 的实现(比如从 UserDaoImpl 换成 UserDaoMockImpl 用于测试),
必须修改 UserServiceImpl 的代码,这就是高耦合。
- 二、"控制反转(IOC)" 的核心思想
"控制反转" ,体现了 "对象创建权的转移": - 原本由 UserServiceImpl 自己创建 UserDaoImpl(程序自身控制);
- 现在把 "创建 UserDao 对象" 的控制权转移给外部容器(比如 Spring 容器)。
这样,UserServiceImpl 不再关心 UserDao 是怎么创建的,只需要 "用" 即可 ------ 这就是 "控制反转"(Inversion of Control)。 - 三、"依赖注入(DI)" 的落地方式
再看左侧的 UserController 类:
java
@RestController
public class UserController {
private UserService userService; // 声明依赖,但不自己创建
// ...
}
(比如 Spring)主动把 UserService 的实例 "注入" 到 UserController 中
------ 这就是 "依赖注入"(Dependency Injection)。

- 以下是用 Spring Boot 框架实现 IOC/DI 分层解耦的最简代码案例,分 Controller、Service、Dao 三层 展示:
-
- 实体类(User.java)
java
public class User {
private Integer id;
private String name;
// 构造方法、getter/setter 省略
public User() {}
public User(Integer id, String name) { this.id = id; this.name = name; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
- Controller 层(控制层)
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController // 标记为 Controller 层 Bean,同时支持 REST 接口
public class UserController {
// 依赖注入:Spring 自动将 UserService 的 Bean 注入到这里 ->在这里进行了解耦
@Autowired
private UserService userService;
@GetMapping("/users")
public List<User> getAllUsers() {
return userService.listUsers(); // 调用 Service 层方法
}
}
-
- Service 层(业务逻辑层)
java
import java.util.List;
public interface UserService {
List<User> listUsers();
}
-
- 实现类:UserServiceImpl.java
java
@Service // 标记为 Service 层 Bean,由 Spring 容器管理
public class UserServiceImpl implements UserService {
// 依赖注入:Spring 自动将 UserDao 的 Bean 注入到这里 ->这里实现了解耦
@Autowired
private UserDao userDao;
@Override
public List<User> listUsers() {
return userDao.findAll(); // 调用 Dao 层方法
}
}
-
- Dao 层(数据访问层)
java
@Repository // 标记为 Dao 层 Bean,由 Spring 容器管理
public class UserDao {
// 模拟从数据库查询用户
public List<User> findAll() {
List<User> userList = new ArrayList<>();
userList.add(new User(1, "张三"));
userList.add(new User(2, "李四"));
return userList;
}
}
- @Component 是 Spring 中最基础的注解,用于标记一个类为 "Spring 管理的组件(Bean)",让 Spring 容器自动创建并管理这个类的实例。
- 它是 @Controller、@Service、@Repository 的 "父注解",这三个注解本质上都是 @Component 的特殊形式(只是语义不同,分别对应控制层、业务层、数据访问层)。

- 解耦的核心就是,把各个实现类都打上注解@Conponent,变成Bean对象,通过@Autowire注入到对应的接口上;

3.IOC 控制反转详解 - IOC(控制反转,Inversion of Control)是一种软件设计思想,核心是将对象的创建、依赖管理和生命周期控制从代码内部转移到外部容器(如 Spring 容器),从而实现模块间的解耦 。

- 一、核心思想:"控制权反转 "
传统开发中,对象 A 若依赖对象 B,需要在 A 内部主动new B()来创建依赖;而在 IOC 思想中,A 不再主动创建 B,而是由外部容器(如 Spring)创建 B 并 "注入" 到 A 中------ 即 "对象的控制权从代码自身反转到外部容器"。 - 二、实现方式:依赖注入(DI)是核心
IOC 的实现手段主要是依赖注入(Dependency Injection),即容器在运行时将对象的依赖主动 "注入" 到需要的地方。 - 三、IOC 容器:管理对象的 "管家"
IOC 容器是实现 IOC 的核心载体(如 Spring 的ApplicationContext),它的核心职责是:- 对象创建:根据配置(注解、XML 等)创建 Bean 对象。
- 依赖注入:解析对象的依赖关系,自动注入所需的 Bean。
- 生命周期管理:控制 Bean 的初始化、销毁等过程,支持单例、原型等作用域。
- IOC 的本质是 "把对象的控制权交给容器,专注于业务逻辑"。通过依赖注入实现解耦,让代码从 "主动创建依赖" 变为 "被动接收依赖",最终达成 "高内聚、低耦合" 的设计目标。这一思想是 Spring 等框架的核心
3.1组件扫描

- 前面通过各种方式把实现类都变成了Bean,那要怎么让bean生效呢? 必须要被@ComponentScan注解扫描
- @ComponentScan 是 Spring 框架中用于自动扫描并注册组件到 IOC 容器的核心注解

- 一、核心作用
自动扫描指定包及其子包下的类,将带有 @Component 及其派生注解(@Service、@Controller、@Repository)的类注册为 Spring Bean,纳入 IOC 容器管理。 - 点进@SpringBootApplication注解

java
1.指定扫描包
@ComponentScan(basePackages = "com.example.service") // 扫描该包及其子包
public class SpringBootWebApplication{
}
2.指定多包扫描
@ComponentScan(basePackages = {"com.example.service", "com.example.controller"})
3.高级过滤:包含 / 排除特定类
这段代码是 Spring Boot 中 @ComponentScan 注解的具体配置,主要用于自定义排除某些类的扫描,
核心是通过两个自定义过滤器(TypeExcludeFilter 和 AutoConfigurationExcludeFilter)
排除不需要注册为 Spring Bean 的类。
@ComponentScan(
excludeFilters = {
@Filter(
type = FilterType.CUSTOM, // 过滤器类型:自定义
classes = {TypeExcludeFilter.class} // 自定义过滤器类
),
@Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class} // 另一个自定义过滤器类
)
}
)
)

- (@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan)是 Spring Boot 的核心注解,共同构成了 @SpringBootApplication 注解的底层实现,也是 Spring Boot 实现 "自动配置" 和 "快速开发" 的关键。
4.1 @SpringBootConfiguration 实现自定义配置类
- @SpringBootConfiguration 是 Spring Boot 对 @Configuration 的封装,用于定义自定义配置类,在类中可以通过 @Bean 注册自定义 Bean。
java
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
/**
* 自定义配置类:演示 @SpringBootConfiguration 的作用
* 功能:注册一个自定义工具类到 Spring 容器
*/
@SpringBootConfiguration // 标识这是一个 Spring Boot 配置类(等价于 @Configuration)
public class CustomConfig {
/**
* 定义一个 Bean:创建 DateUtil 实例并交由 Spring 管理
* @return DateUtil 实例
*/
@Bean
public DateUtil dateUtil() {
return new DateUtil(); // Spring 容器会管理这个对象的生命周期
}
}
// 测试类:验证自定义 Bean 的注入
@RestController
public class TestController {
// 注入自定义配置类中注册的 DateUtil Bean
@Autowired
private CustomConfig.DateUtil dateUtil;
}
@SpringBootConfiguration:标记类为 Spring Boot 配置类,
Spring 会扫描其中的 @Bean 方法,将返回的对象注册为容器中的 Bean。
4.2 EnableAutoConfiguration 实现自动配置(模拟第三方 Starter)
- @EnableAutoConfiguration 是 Spring Boot 自动配置的核心,它会根据项目依赖自动加载并配置相关组件。
- @EnableAutoConfiguration 自动加载数据库的例子
- 数据源自动配置:DataSourceAutoConfiguration 类会读取 application.yml 中的 spring.datasource 配置,自动创建 DataSource 实例
配置数据库连接(application.yml)
java
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
username: root
password: your_password
核心逻辑说明

- DataSourceAutoConfiguration 读取配置并创建 DataSource:该类会读取 application.yml 中 spring.datasource 前缀的配置,自动创建 DataSource 实例
4.3 小结
- @EnableAutoConfiguration 是 "自动模式",根据依赖和配置自动生成环境所需的 Bean(如数据源、Web 组件);
- @SpringBootConfiguration 是 "手动模式",允许开发者主动定义配置类,通过 @Bean 手动注册特定 Bean。
5.DI 依赖注入
- 依赖注入(DI,Dependency Injection)是控制反转(IOC)的具体实现方式,核心是 "容器在运行时自动将对象所需的依赖(其他对象)传递给它,而不是由对象自己创建依赖"。
- 以下是基于接口的依赖注入(DI)代码示例,通过 "接口定义 + 实现类 + 注入依赖" 的方式,体现低耦合的设计思想:
java
1. 定义接口(抽象依赖)
// 用户服务接口(抽象行为定义)
public interface UserService {
String getUserName(Long userId);
}
2. 实现接口(具体依赖)
// 接口的实现类(具体业务逻辑)
@Service // 注册为 Spring Bean,由容器管理
public class UserServiceImpl implements UserService {
@Override
public String getUserName(Long userId) {
// 模拟从数据库查询
return "用户" + userId + ":张三";
}
}
3.// 构造器注入(推荐方式,强制依赖不可变)
@Autowired // Spring 自动注入 UserService 的实现类(UserServiceImpl)
public UserController(UserService userService) {
this.userService = userService;
}
关键说明
1.依赖接口而非实现:UserController 只依赖 UserService 接口,不直接依赖 UserServiceImpl,降低了耦合度。
2.灵活替换实现:若需要更换业务逻辑,只需新增一个实现类(如 UserServiceMockImpl),
并注册为 Bean,UserController 无需任何修改:
5.1 三种注入方式 - 主要还是用属性注入


