目录

  • 分层解耦
    • 案例:将 emp.xml 中的数据解析并响应
    • 三层架构
    • 分层解耦
    • IOC & DI 入门
    • IOC 详解
    • DI 详解

分层解耦

案例:将 emp.xml 中的数据解析并响应

emp.xml 内容如下:

<emps><emp><name>Tom</name><age>18</age><gender>1</gender><job>1</job></emp><emp><name>Jerry</name><age>19</age><gender>1</gender><job>2</job></emp><emp><name>Mike</name><age>20</age><gender>1</gender><job>3</job></emp><emp><name>Lucy</name><age>21</age><gender>2</gender><job>3</job></emp>
</emps>

引入相关依赖:

<dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version>
</dependency>

XML 文件解析工具类 XmlParserUtils:

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;public class XmlParserUtils {public static <T> List<T> parse(String file, Class<T> targetClass){ArrayList<T> list = new ArrayList<>();try {SAXReader saxReader = new SAXReader();Document document = saxReader.read(new File(file));Element rootElement = document.getRootElement();List<Element> elements = rootElement.elements("emp");for (Element element : elements) {String name = element.element("name").getText();String age = element.element("age").getText();String gender = element.element("gender").getText();String job = element.element("job").getText();Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, Integer.class, String.class, String.class);constructor.setAccessible(true);T object = constructor.newInstance(name, Integer.parseInt(age), gender, job);list.add(object);}} catch (Exception e) {e.printStackTrace();}return list;}
}

实体类 Emp:

public class Emp {private String name;private Integer age;private String gender;private String job;public Emp(String name, Integer age, String gender, String job) {this.name = name;this.age = age;this.gender = gender;this.job = job;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getJob() {return job;}public void setJob(String job) {this.job = job;}@Overridepublic String toString() {return "Emp{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", job='" + job + '\'' +'}';}
}

统一响应结果封装类 Result:

/** 统一响应结果封装类*/
public class Result {private Integer code; // 状态码,200为成功,500为失败private String message; // 返回消息private Object data; // 返回数据public Result(Integer code, String message, Object data) {this.code = code;this.message = message;this.data = data;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static Result success(Object data) {return new Result(200, "success", data);}public static Result success() {return new Result(200, "success", null);}public static Result error(String message) {return new Result(500, message, null);}@Overridepublic String toString() {return "Result{" +"code=" + code +", message='" + message + '\'' +", data=" + data +'}';}
}

请求处理类 EmpController:

import com.example.demo.pojo.Emp;
import com.example.demo.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;@RestController
public class EmpController {@RequestMapping("/listEmp")public Result list() {// 1. 加载并解析emp.xmlString file = this.getClass().getClassLoader().getResource("emp.xml").getFile();List<Emp> empList = XmlParserUtils.<Emp>parse(file, Emp.class);// 2. 对数据进行转换处理empList.stream().forEach(emp -> {String gender = emp.getGender();if ("1".equals(gender)) {emp.setGender("男");} else if ("2".equals(gender)) {emp.setGender("女");}String job = emp.getJob();if ("1".equals(job)) {emp.setJob("讲师");} else if ("2".equals(job)){emp.setJob("班主任");} else if ("3".equals(job)){emp.setJob("就业指导");}});//3. 响应数据return Result.success(empList);}
}

响应结果为:

在这里插入图片描述

三层架构

controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据

service:业务逻辑层,处理具体的业务逻辑

dao:数据访问层(Data Access Object,持久层),负责数据访问操作,包括数据的增删改查

在这里插入图片描述

回顾上述案例,会发现请求控制、业务逻辑处理和数据访问都在同一个类中,这样就会使代码的复用性差以及难以维护,所以按照三层架构改为以下内容:

  1. dao 层接口 EmpDao:

    import com.example.demo.pojo.Emp;
    import java.util.List;public interface EmpDao {// 获取员工列表数据public List<Emp> listEmp();
    }
    
  2. dao 层实现类 EmpDaoA:

    import com.example.demo.dao.EmpDao;
    import com.example.demo.pojo.Emp;
    import com.example.demo.utils.XmlParserUtils;
    import java.util.List;public class EmpDaoA implements EmpDao {@Overridepublic List<Emp> listEmp() {// 1. 加载并解析emp.xmlString file = this.getClass().getClassLoader().getResource("emp.xml").getFile();List<Emp> empList = XmlParserUtils.<Emp>parse(file, Emp.class);return empList;}
    }
    
  3. service 层接口 EmpService:

    import com.example.demo.pojo.Emp;
    import java.util.List;public interface EmpService {public List<Emp> listEmp();
    }
    
  4. service 层实现类 EmpServiceA:

    import com.example.demo.dao.EmpDao;
    import com.example.demo.dao.impl.EmpDaoA;
    import com.example.demo.pojo.Emp;
    import com.example.demo.service.EmpService;
    import java.util.List;public class EmpServiceA implements EmpService {private EmpDao empDao = new EmpDaoA();@Overridepublic List<Emp> listEmp() {// 1. 调用dao层方法获取数据List<Emp> empList = empDao.listEmp();// 2. 对数据进行转换处理empList.stream().forEach(emp -> {String gender = emp.getGender();if ("1".equals(gender)) {emp.setGender("男");} else if ("2".equals(gender)) {emp.setGender("女");}String job = emp.getJob();if ("1".equals(job)) {emp.setJob("讲师");} else if ("2".equals(job)){emp.setJob("班主任");} else if ("3".equals(job)){emp.setJob("就业指导");}});return empList;}
    }
    
  5. controller 层实现类 EmpController:

    import com.example.demo.pojo.Emp;
    import com.example.demo.service.EmpService;
    import com.example.demo.service.impl.EmpServiceA;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;@RestController
    public class EmpController {private EmpService empService = new EmpServiceA();@RequestMapping("/listEmp")public Result list() {List<Emp> empList = empService.listEmp();//3. 响应数据return Result.success(empList);}
    }
    

这样就完成了三层架构的分层,便于后期的管理与维护。

分层解耦

内聚:指一个模块(类、方法、函数等)内部各个元素(如方法、属性)之间的关联紧密程度。内聚度越高,说明模块内部的功能越集中、单一,“做一件事并做好”;内聚度低则意味着模块功能混乱,职责不清晰。

耦合:指不同模块之间的依赖紧密程度。耦合度越低,模块之间的独立性越强,一个模块的修改对其他模块的影响越小;耦合度高则意味着模块 “牵一发而动全身”,难以维护。

在这里插入图片描述

按照案例中三层架构的流程,controller 中要调用 service 都需要创建一个 service 层的对象,controller 层与 service 层耦合;service 中要调用 dao 都需要创建一个 dao 层的对象,service 层与 dao 层耦合。若后续需要更换 EmpService 的实现类(比如新增 EmpServiceB),必须修改 EmpController 的代码;当 Dao 层实现变更(如换成 EmpDaoB),得改动 EmpServiceA 代码,耦合度高,不利于代码扩展和维护。

为了解决这个问题,就要引入三个概念:控制反转、依赖注入、Bean 对象:

  • 控制反转:Inversion Of Control,简称 IOC,对象的创建控制权由程序自身转移到外部(容器),这种思想成为控制反转。
  • 依赖注入:Dependency Injection,简称 DI,容器为应用程序提供运行时所依赖的资源,称为依赖注入。
  • Bean 对象:IOC 容器中创建、管理的对象,称为 Bean。

IOC & DI 入门

操作步骤:

  1. service 层及 dao 层的实现类,交给 IOC 容器管理,使用 @Component 注解
  2. 为 controller 层及 service 层注入运行时依赖的对象,使用 @Autowired 注解
  3. 运行测试

代码做以下修改:

  1. controller 层

    import com.example.demo.pojo.Emp;
    import com.example.demo.service.EmpService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;@RestController
    public class EmpController {@Autowiredprivate EmpService empService;@RequestMapping("/listEmp")public Result list() {List<Emp> empList = empService.listEmp();//3. 响应数据return Result.success(empList);}
    }
    
  2. service 层

    import com.example.demo.dao.EmpDao;
    import com.example.demo.pojo.Emp;
    import com.example.demo.service.EmpService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.util.List;@Component
    public class EmpServiceA implements EmpService {@Autowiredprivate EmpDao empDao;@Overridepublic List<Emp> listEmp() {// 1. 调用dao层方法获取数据List<Emp> empList = empDao.listEmp();// 2. 对数据进行转换处理empList.stream().forEach(emp -> {String gender = emp.getGender();if ("1".equals(gender)) {emp.setGender("男");} else if ("2".equals(gender)) {emp.setGender("女");}String job = emp.getJob();if ("1".equals(job)) {emp.setJob("讲师");} else if ("2".equals(job)){emp.setJob("班主任");} else if ("3".equals(job)){emp.setJob("就业指导");}});return empList;}
    }
    
  3. dao 层

    import com.example.demo.dao.EmpDao;
    import com.example.demo.pojo.Emp;
    import com.example.demo.utils.XmlParserUtils;
    import org.springframework.stereotype.Component;
    import java.util.List;@Component
    public class EmpDaoA implements EmpDao {@Overridepublic List<Emp> listEmp() {// 1. 加载并解析emp.xmlString file = this.getClass().getClassLoader().getResource("emp.xml").getFile();List<Emp> empList = XmlParserUtils.<Emp>parse(file, Emp.class);return empList;}
    }
    

这样就完成了模块间的解耦操作

IOC 详解

Bean 的声明

要把某个对象交给 IOC 容器管理,需要在对应的类上加上如下注解之一:

注解说明位置
@Component声明 Bean 的基础注解不属于以下三类时,用此注解
@Controller@Component 的衍生注解标注在控制器类上(@RestController 已包含)
@Service@Component 的衍生注解标注在业务类上
@Repository@Component 的衍生注解标注在数据访问类上(由于与 Mybatis 整合,用的少)

声明控制器 Bean 只能用 @Controller

在 IOC 容器中,每个 Bean 都有唯一标识,就是类名首字母小写

在这里插入图片描述

当然也可以自己定义 Bean 的名称,只需要在注解后面加上 value

@Repository(value = "daoA") // value也可以不写
public class EmpDaoA implements EmpDao {//...
}

添加之后,Bean 的名称就变为自己定义的

在这里插入图片描述

Bean 的组件扫描:

  • 前面声明 Bean 的四大注解,若要想生效,需要被组件扫描注解 @ComponentScan 扫描
  • @ComponentScan 注解虽然没有显示配置,但实际上已包含在启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包

DI 详解

@Autowired 注解,默认是按照类型进行,如果存在多个相同类型的 Bean,将会报出如下错误:

Field empService in com.itheima.controller.EmpController required a single bean, but 2 were found:- empServiceA: defined in file [E:\springboot - web - req - resp\target\classes\com\itheima\service\impl\EmpServiceA.class- empServiceB: defined in file [E:\springboot - web - req - resp\target\classes\com\itheima\service\impl\EmpServiceB.classAction:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean

可以通过以下几个方案来解决:

  • @Primary:如果有多个相同类型的 Bean,@Primary 加在哪个 Bean 上,哪个 Bean 就生效

    @Primary
    @Service
    public class EmpServiceA implements EmpService {@Autowiredprivate EmpDao empDao;@Overridepublic List<Emp> listEmp() {//...}
    }
    
  • @Qualifier:放在 Bean 内,与 @Autowired 配合使用,@Qualifier(“Bean 的名称”)

    @Service
    public class EmpServiceA implements EmpService {@Qualifier("daoA")@Autowiredprivate EmpDao empDao;@Overridepublic List<Emp> listEmp() {// ...}
    }
    
  • @Resource:用 @Resource(name = “Bean 的名称”) 来替换 @Autowired

    @Service
    public class EmpServiceA implements EmpService {@Resource(name = "daoA")private EmpDao empDao;@Overridepublic List<Emp> listEmp() {// ...}
    }
    

@Resource 和 @Autowired 的区别:

  • @Autowired 是 Spring 框架提供的注解,而 @Resource 是 JDK 提供的注解
  • @Autowired 是默认按照类型注入,而 @Resource 是默认按照名称注入

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91581.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91581.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91581.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

井云科技2D交互数字人:让智能服务触手可及的实用方案

在如今的数字化时代&#xff0c;智能交互已成为各行业提升服务质量的重要方向。而井云 2D 交互数字人系统凭借其独特的技术优势&#xff0c;正逐渐成为众多企业实现智能服务升级的优选。它无需复杂的操作和高昂的成本&#xff0c;就能让数字人在各类线下场景中发挥重要作用&…

本地部署VMware ESXi,并实现无公网IP远程访问管理服务器

ESXi&#xff08;VMware ESXi&#xff09;是VMware公司推出的一款企业级虚拟化平台&#xff0c;基于裸机&#xff08;bare-metal&#xff09;安装的虚拟化操作系统。它可以在一台物理服务器上运行多个虚拟机&#xff0c;广泛应用于数据中心和云计算环境中。很多公司为了方便管理…

让科技之光,温暖银龄岁月——智绅科技“智慧养老进社区”星城国际站温情纪实

七月的风&#xff0c;带着夏日的热情&#xff0c;轻轻拂过邯郸星城国际社区葱郁的绿意。2025年7月30日&#xff0c;一个以“幸福晚景&#xff0c;乐享银龄—智慧养老进社区”为主题的活动&#xff0c;如一股暖流&#xff0c;浸润了社区的长者们。智绅科技怀揣着“科技赋能养老&…

Java单元测试和设计模式

单元测试 . 测试分类 什么是测试? 测试的目的是尽可能多的发现软件中存在的BUG,而不是为了隐藏BUG。事实上测试有很多种类,比如:边界测试,压力测试,性能测试等 黑盒测试 黑盒测试也叫功能测试,主要关注软件每个功能是否实现,并不关注软件代码是否有错误;测试人员…

UOS统信桌面系统解决编译错误:C compiler cc is not found指南

一、系统环境 1.操作系统版本2.编译环境 PC:~$ gcc --version gcc (Uos 8.3.0.13-deepin1) 8.3.0 Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY o…

深入理解 Docker 容器网络:为什么用 host 网络模式能解决连通性问题?

Docker 已经成为现代应用部署的标配&#xff0c;大家都知道它的网络隔离做得很好&#xff0c;既安全又灵活。不过&#xff0c;在实际用 Docker 部署服务的过程中&#xff0c;相信很多人都遇到过这样的情况&#xff1a;主机上能连通的外部服务&#xff0c;一到容器里却死活连不上…

Spring Boot 异常处理:从全局捕获到优化用户体验!

全文目录&#xff1a;开篇语**前言****1. Spring Boot 异常处理的基本概念****2. 使用 ExceptionHandler 局部处理异常****示例&#xff1a;局部异常处理****优化建议&#xff1a;****3. 使用 ControllerAdvice 和 RestControllerAdvice 进行全局异常处理****示例&#xff1a;全…

vue3.0 + TypeScript 中使用 axios 同时进行二次封装

项目背景是vite搭建的vue3.0 TypeScript 的项目&#xff0c;需要统一处理和统一维护就对axios进行了二次封装 axios的安装 npm install axios定义http文件夹然后内部定义index.ts文件&#xff0c;内部开始封装 import axios, {type AxiosInstance} from "axios";…

ESP32- 项目应用1 音乐播放器之sd的驱动配置 #1

音乐播放器 ESP32- 项目应用1 音乐播放器之sd的驱动配置 #1 文章目录 音乐播放器 1 sd卡介绍 1.1 SDCARD介绍 1.2 物理结构 1.3 协议说明 1.4 sd 卡模式 1.5 数据模式 1.6 sdio 初始化流程 1.7 SPI 模式下的 SD 卡初始化 2 原理图 2.1 sd原理图 2.2 esp32的接口 3 代码配置 3.…

Vue.js 指令系统完全指南:深入理解 v- 指令

Vue.js 的指令系统是其最强大的特性之一&#xff0c;通过以 v- 开头的特殊属性&#xff0c;我们可以在模板中声明式地绑定底层Vue实例的数据。本文将深入讲解Vue中最重要的指令&#xff0c;帮助掌握Vue的核心功能。 文章目录1. v-model&#xff1a;双向数据绑定的核心基本用法修…

计算机分类汇总大全

前端部分有 Node.js、ActionScript、Swift、TypeScript、Webpack、JavaScript。需要分别详细说明它们的定义、特点、应用场景、优缺点等。比如 Node.js&#xff0c;要提到它的运行环境、事件驱动、非阻塞 I/O&#xff0c;适合的应用如 API 服务、实时应用&#xff0c;以及常用框…

模拟链路滤波器设计一些细节

目录 原设计思路剖析 300M带宽仿真与计算 原设计思路剖析 滤波器设计的一些细节,以下为ADS54J60模拟链路的一些问题设计原理图 实际电路设计如上所示,但是实际bom并未按此设计,根据实际的BOM明细以及ADC使用说明书,可以间接理解原设计者的设计初衷,是将ADC的一部分特性…

CatBoost 完整解析:类别特征友好的梯度提升框架

1️⃣ 什么是 CatBoost&#xff1f;CatBoost&#xff08;Categorial Boosting&#xff09;是由 Yandex&#xff08;俄罗斯搜索引擎公司&#xff09;开源的梯度提升框架&#xff0c;专为类别特征处理优化。核心特点&#xff1a;无需手动 one-hot / LabelEncoding&#xff0c;原生…

NDBmysql-cluster集群部署脚本

NDB(Network Database)是MySQL Cluster的核心存储引擎,专为高可用性、高吞吐量、分布式数据存储设计,采用内存计算+磁盘持久化架构,支持跨节点数据分片与自动冗余,适用于对实时性和可靠性要求严苛的业务场景(如金融交易、电信计费、实时分析等)。 今天大白鼠就分享构建…

计算机网络中的socket是什么?编程语言中的socket编程又是什么?python的socket编程又该如何用?

1. 计算机网络中的 Socket 是什么&#xff1f; 想象一下电话系统&#xff1a; 电话插座 (Socket)&#xff1a; 是墙上的一个物理接口&#xff0c;它本身不是通话&#xff0c;但它是建立通话连接的端点。你需要把电话线插进插座才能打电话。通话 (Connection)&#xff1a; 是两台…

【科普】进程与线程的区别

一、定义与概念&#xff1a;进程&#xff1a;进程是执行中的一段程序。一旦程序被载入到内存中并准备执行&#xff0c;就变成了一个进程。进程是表示资源分配的基本概念&#xff0c;又是调度运行的基本单位&#xff0c;是系统中的并发执行的单位。线程&#xff1a;线程是进程中…

Conda创建虚拟环境,解决不同项目的冲突!

随着需求的增多&#xff0c;又要增加多几个不同的项目来在本地测试&#xff0c;这个时候往往就会遇到 不同项目的Python版本不同等等一系列问题&#xff0c;只好请出Conda来帮忙。 一、先去下载安装一下Conda Conda | Anaconda.org 安装完后&#xff0c;需要给CONDA做个环境变…

RK3568下的进程间广播通信:用C语言构建简单的中心服务器

最近的项目中需要实现这样一个功能,就是一个进程A发消息,进程B和进程C都能收到相同的消息,同样,进程B发消息,进程A和进程C也都能收到消息,就像下图中的描述。 使用一个中心服务器作为消息转发枢纽,所有客户端连接到服务器,发送消息到服务器后,服务器再将消息转发给所有…

激光雷达/相机一体机 时间同步和空间标定(1)

一、参考链接 海康威视、LIVOX与PTP时间同步_海康相机ptp同步-CSDN博客 基于PTP实现主机与相机系统时钟同步功能_ptp同步-CSDN博客

嵌入式系统的中断控制器(NVIC)

1. NVIC的核心功能 核心价值&#xff1a;NVIC通过硬件级中断管理、自动状态处理及低延迟优化&#xff0c;为实时系统提供确定性响应&#xff0c;是Cortex-M芯片实时性的基石。 中断优先级管理 支持多级可编程优先级&#xff08;通常4-8位&#xff0c;如STM32用4位实现16级优先级…