当前位置: 首页 > news >正文

黑马JAVAWeb-03 SpringBootWeb-分层解耦-三层架构-@SpringBootApplication注解-IOC控制反转-DI依赖注入

1.三层架构

  • 在单层架构中,数据访问,逻辑处理和请求响应均放在同一类中
    在这里插入图片描述
    在这里插入图片描述
  • 三层架构
    在这里插入图片描述
  • 以下按照 实体类 → Controller 层 → Service 层 → Dao 层 的顺序重新整理代码,包含 @RestController 及详细注解,基于 Spring Boot 框架规范实现:
  1. 实体类(User.java)
import lombok.Data;/*** 用户实体类(POJO)* 用于封装用户数据,作为各层之间的数据传输载体*/
@Data  // Lombok注解:自动生成getter、setter、toString、equals等方法,简化代码
public class User {private Integer id;         // 用户唯一标识IDprivate String username;    // 用户名(登录账号)private String password;    // 密码(实际开发中需加密存储,如BCrypt加密)private String nickname;    // 用户昵称(显示用)
}
  1. Controller 层(UserController.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,降低耦合度,便于测试和扩展*/@Autowiredprivate 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();}
}
    1. Service 层
      接口:UserService.java
/*** 用户业务逻辑接口* 定义用户相关的业务操作规范,隔离Controller层与数据访问层*/
public interface UserService {/*** 根据ID查询用户* @param id 用户ID* @return 用户实体(若不存在则抛出异常)*/User getUserById(Integer id);/*** 新增用户* @param user 待新增的用户数据(不含ID,由数据库自动生成)* @return 新增后的用户数据(包含ID)*/User addUser(User user);
}
    1. 实现类:UserServiceImpl.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** 用户业务逻辑实现类* 处理核心业务逻辑(如参数校验、事务控制等),调用Dao层操作数据*/
@Service  // Spring注解:标识为业务层组件,交由Spring容器管理
public class UserServiceImpl implements UserService {/*** 依赖注入:自动获取UserDao实例*/@Autowiredprivate UserDao userDao;@Overridepublic 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;}@Overridepublic User addUser(User user) {// 业务校验:用户名不能为空if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {throw new IllegalArgumentException("用户名不能为空");}// 调用Dao层插入数据userDao.insert(user);// 返回插入后的用户(包含自动生成的ID)return user;}
}
    1. Dao 层(数据访问层)-注解的方式
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);
}
    1. Dao 层(数据访问层) -接口-实现类的形式
      接口:UserDao.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);
}
    1. 实现类:(由 MyBatis 自动生成,无需手动编写)
      MyBatis 会通过接口和 XML 映射文件动态生成实现类,无需开发者手动编写实现代码。核心是通过 XML 文件定义 SQL 与接口方法的映射关系。
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 类:
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 类:
@RestController
public class UserController {private UserService userService; // 声明依赖,但不自己创建// ...
}
(比如 Spring)主动把 UserService 的实例 “注入” 到 UserController 中
—— 这就是 “依赖注入”(Dependency Injection)。

在这里插入图片描述

  • 以下是用 Spring Boot 框架实现 IOC/DI 分层解耦的最简代码案例,分 Controller、Service、Dao 三层 展示:
    1. 实体类(User.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; }
}
  1. Controller 层(控制层)
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 注入到这里 ->在这里进行了解耦@Autowiredprivate UserService userService;@GetMapping("/users")public List<User> getAllUsers() {return userService.listUsers(); // 调用 Service 层方法}
}
    1. Service 层(业务逻辑层)
import java.util.List;public interface UserService {List<User> listUsers();
}
    1. 实现类:UserServiceImpl.java
@Service // 标记为 Service 层 Bean,由 Spring 容器管理
public class UserServiceImpl implements UserService {// 依赖注入:Spring 自动将 UserDao 的 Bean 注入到这里  ->这里实现了解耦@Autowiredprivate UserDao userDao;@Overridepublic List<User> listUsers() {return userDao.findAll(); // 调用 Dao 层方法}
}
    1. Dao 层(数据访问层)
@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注解
    在这里插入图片描述
1.指定扫描包
@ComponentScan(basePackages = "com.example.service") // 扫描该包及其子包
public class SpringBootWebApplication{
}2.指定多包扫描
@ComponentScan(basePackages = {"com.example.service", "com.example.controller"})3.高级过滤:包含 / 排除特定类
这段代码是 Spring Boot@ComponentScan 注解的具体配置,主要用于自定义排除某些类的扫描,
核心是通过两个自定义过滤器(TypeExcludeFilterAutoConfigurationExcludeFilter)
排除不需要注册为 Spring Bean 的类。
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,  // 过滤器类型:自定义classes = {TypeExcludeFilter.class}  // 自定义过滤器类),@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}  // 另一个自定义过滤器类)}
)
)

在这里插入图片描述
4. (@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan)是 Spring Boot 的核心注解,共同构成了 @SpringBootApplication 注解的底层实现,也是 Spring Boot 实现 “自动配置” 和 “快速开发” 的关键。
4.1 @SpringBootConfiguration 实现自定义配置类

  • @SpringBootConfiguration 是 Spring Boot 对 @Configuration 的封装,用于定义自定义配置类,在类中可以通过 @Bean 注册自定义 Bean。
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 实例*/@Beanpublic DateUtil dateUtil() {return new DateUtil();  // Spring 容器会管理这个对象的生命周期}
}// 测试类:验证自定义 Bean 的注入
@RestController
public class TestController {// 注入自定义配置类中注册的 DateUtil Bean@Autowiredprivate 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)

spring:datasource:url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTCusername: rootpassword: your_password

核心逻辑说明
在这里插入图片描述

  • DataSourceAutoConfiguration 读取配置并创建 DataSource:该类会读取 application.yml 中 spring.datasource 前缀的配置,自动创建 DataSource 实例

4.3 小结

  • @EnableAutoConfiguration 是 “自动模式”,根据依赖和配置自动生成环境所需的 Bean(如数据源、Web 组件);
  • @SpringBootConfiguration 是 “手动模式”,允许开发者主动定义配置类,通过 @Bean 手动注册特定 Bean。

5.DI 依赖注入

  • 依赖注入(DI,Dependency Injection)是控制反转(IOC)的具体实现方式,核心是 “容器在运行时自动将对象所需的依赖(其他对象)传递给它,而不是由对象自己创建依赖”。
  • 以下是基于接口的依赖注入(DI)代码示例,通过 “接口定义 + 实现类 + 注入依赖” 的方式,体现低耦合的设计思想:
1. 定义接口(抽象依赖)
// 用户服务接口(抽象行为定义)
public interface UserService {String getUserName(Long userId);
}
2. 实现接口(具体依赖)
// 接口的实现类(具体业务逻辑)
@Service // 注册为 Spring Bean,由容器管理
public class UserServiceImpl implements UserService {@Overridepublic String getUserName(Long userId) {// 模拟从数据库查询return "用户" + userId + ":张三";}
}
3.// 构造器注入(推荐方式,强制依赖不可变)@Autowired // Spring 自动注入 UserService 的实现类(UserServiceImpl)public UserController(UserService userService) {this.userService = userService;}关键说明
1.依赖接口而非实现:UserController 只依赖 UserService 接口,不直接依赖 UserServiceImpl,降低了耦合度。
2.灵活替换实现:若需要更换业务逻辑,只需新增一个实现类(如 UserServiceMockImpl),
并注册为 BeanUserController 无需任何修改:

5.1 三种注入方式 - 主要还是用属性注入
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/569899.html

相关文章:

  • 网站评论列表模板公司logo图标
  • Linux_Socket_TCP
  • 拼多多福利券小程序怎么赚钱潍坊seo管理
  • JAVA国际版同城外卖跑腿团购到店跑腿多合一APP系统源码支持Android+IOS+H5
  • 做电锯电音的网站古董手表网站
  • 电力工程设计AI推荐:良策金宝AI以“六大智能”重塑行业效率
  • Yolo12改进策略:下采样改进|IPFA,下采样|信息保留特征聚合模块|即插即用
  • 网站seo内部优化怎么推广平台
  • 零陵区住房和城乡建设局网站百度网址域名大全
  • 0基础学舞蹈,学习计划
  • Redis_4_常见命令(完)+认识数据类型和编码方式
  • 代码交易网站邯郸网站建设费用
  • 黑色网站源码三河市网站建设
  • 20251104让AIO-3576Q38开发板跑Rockchip的原厂Android14之后适配GPIO扩展芯片PCA9555
  • Python基于PyTorch实现多输入多输出进行LSTM循环神经网络回归预测项目实战
  • Hadess零基础学习,如何管理Helm制品
  • 今日行情明日机会——20251104
  • 校园网站建设多少钱网站的公告轮播效果怎么做
  • 网站演示程序上海广告公司招聘信息
  • 中小企业等保合规成本控制:上海云盾低成本安全建设方案
  • MATLAB实现灰度图像二维傅里叶变换
  • Photoshop通道中的基本操作
  • YOLOv5(PyTorch)目标检测实战:TensorRT加速部署!训练自己的数据集(Ubuntu)——(人工智能、深度学习、机器学习、神经网络)
  • 网站推广与优化怎么做大型平面设计网站
  • STM32H743-ARM例程37-NETIO
  • golang 网站开发 教程自己做网站代理产品
  • 构建1688店铺商品数据集:Python爬虫数据采集与格式化实践
  • JavaEE初阶——多线程(5)单例模式和阻塞队列
  • Dart | 安装基础环境和快速入门(保姆级教程)
  • 网站购买域名八年级信息网站怎么做