【Spring Boot 快速入門】三、分層解耦

目錄

  • 分層解耦
    • 案例:將 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級優先級…