欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot?Web開發(fā)之請求響應、分層解耦問題記錄

 更新時間:2024年08月13日 10:42:40   作者:DAWN_T17  
在?Spring?Boot?的?Web?請求響應處理中,Servlet?起著關鍵的作用,Servlet?是?Java?Web?開發(fā)中的基本組件,主要負責處理客戶端的請求并生成響應,這篇文章主要介紹了SpringBoot?Web開發(fā)之請求響應,分層解耦,需要的朋友可以參考下

一.請求響應概述

1.Servlet

在 Spring Boot 的 Web 請求響應處理中,Servlet 起著關鍵的作用。

Servlet 是 Java Web 開發(fā)中的基本組件,主要負責處理客戶端的請求并生成響應。

具體來說,它具有以下重要作用:

  • 接收請求:Servlet 能夠接收來自客戶端(如瀏覽器)發(fā)送的 HTTP 請求。
  • 處理請求:在接收到請求后,執(zhí)行相應的業(yè)務邏輯處理。這可能包括與數(shù)據(jù)庫交互、進行數(shù)據(jù)計算、驗證用戶輸入等操作。
  • 控制流程:根據(jù)請求的類型和參數(shù),決定后續(xù)的處理流程和響應方式。
  • 生成響應:處理完請求后,生成要返回給客戶端的響應數(shù)據(jù)。
  • 與其他組件協(xié)作:可以與其他的 Java 類、服務或組件進行協(xié)作,以完成復雜的業(yè)務功能。

例如,在一個用戶登錄的場景中,Servlet 接收到用戶提交的登錄表單數(shù)據(jù),然后驗證用戶名和密碼是否正確。如果正確,生成一個成功登錄的響應;如果不正確,生成一個錯誤提示的響應。

在 Spring Boot 中,DispatcherServlet 是一個特殊的 Servlet,它負責協(xié)調和分發(fā)請求到具體的控制器(Controller)進行處理,使得整個請求處理流程更加清晰和高效。

Dispatcher:調度員;調度程序;發(fā)送器

2.DispatcherServlet

在 Spring Boot 中,DispatcherServlet 是一個核心組件,起著非常重要的作用

DispatcherServlet 主要負責接收客戶端的請求,并將請求分發(fā)給相應的處理器(Handler)進行處理。它是 Spring Web MVC 框架的前端控制器。

其工作流程大致如下:

當客戶端發(fā)送一個 HTTP 請求到應用程序時,DispatcherServlet 首先會接收到這個請求。然后,它會根據(jù)請求的 URL 和其他相關信息,通過一系列的映射規(guī)則,來確定應該調用哪個控制器(Controller)來處理這個請求。

在確定了控制器之后,DispatcherServlet 會將請求傳遞給對應的控制器方法進行處理??刂破魈幚硗暾埱蠛?,通常會返回一個模型(Model)和視圖(View)的信息。

DispatcherServlet 接著會根據(jù)返回的視圖信息,選擇合適的視圖解析器(View Resolver)來將模型數(shù)據(jù)渲染成最終的響應頁面,并將響應返回給客戶端。

例如,如果有一個用戶請求獲取商品列表的頁面,DispatcherServlet 會找到處理該請求的商品控制器,然后由控制器獲取商品數(shù)據(jù)并返回給 DispatcherServlet ,DispatcherServlet 再通過視圖解析器將數(shù)據(jù)展示在相應的頁面上。

總之,DispatcherServlet 是 Spring Boot 中實現(xiàn) Web 應用請求處理和響應生成的關鍵環(huán)節(jié),確保了整個 Web 應用的流暢運行和高效響應。

 DispatcherServlet 類繼承了Servlet 接口

3.請求響應工作概圖

 DispatcherServlet會根據(jù)請求調度Controller控制器,然后獲得響應的數(shù)據(jù)

(1)HttpServletRequest 是 Java Servlet 規(guī)范中定義的一個接口,用于表示客戶端發(fā)送到服務器的 HTTP 請求。

它包含了大量與請求相關的信息和方法:

請求方法:例如 GETPOST、PUT、DELETE 等,通過 getMethod() 方法獲取。
請求 URL:可以通過 getRequestURI() 方法獲取請求的資源路徑,getQueryString() 方法獲取查詢字符串。
請求頭信息:如 User-Agent(客戶端瀏覽器和操作系統(tǒng)信息)、Content-Type(請求體的數(shù)據(jù)類型)等,使用 getHeader(String name) 方法獲取指定的請求頭。
請求參數(shù):包括表單提交的參數(shù)、URL 中的參數(shù)等,通過 getParameter(String name) 方法獲取單個參數(shù)值,getParameterValues(String name) 方法獲取具有多個值的參數(shù)。

例如,在一個登錄頁面中,用戶輸入用戶名和密碼后提交表單,服務器端可以通過 HttpServletRequest 來獲取用戶名和密碼的參數(shù)值,進行后續(xù)的驗證處理。

另外,如果客戶端發(fā)送了一個帶有特定 Cookie 的請求,服務器可以通過 getCookies() 方法獲取這些 Cookie 信息,從而實現(xiàn)會話跟蹤等功能。

HttpServletRequest 為服務器端處理客戶端的 HTTP 請求提供了豐富的信息和操作方法,是構建 Web 應用的重要組成部分。

(2)HttpServletResponse 是 Java Servlet 規(guī)范中定義的一個接口,用于表示服務器對客戶端 HTTP 請求的響應。

它包含了一系列方法,用于設置響應的狀態(tài)碼、響應頭信息、響應體內容等。

一些常見的方法包括:

  • setStatus(int status):設置響應的狀態(tài)碼,例如 200 表示成功,404 表示未找到資源,500 表示服務器內部錯誤等。
  • setHeader(String name, String value):設置響應頭信息,如設置 Content-Type 來指定響應體的數(shù)據(jù)類型。
  • getWriter():獲取一個 PrintWriter 對象,用于向響應體中寫入字符數(shù)據(jù)。getOutputStream():獲取一個 ServletOutputStream 對象,用于向響應體中寫入二進制數(shù)據(jù)。  

4.BS/CS架構

BS即“Browser/Server”(瀏覽器/服務器模式) :在這種模式下,用戶通過瀏覽器訪問服務器上的應用程序??蛻舳酥饕撠燂@示數(shù)據(jù)和接收用戶輸入,而大部分的業(yè)務邏輯和數(shù)據(jù)處理都在服務器端完成。例如常見的各類網(wǎng)站、在線辦公系統(tǒng)等。其優(yōu)點包括易于維護和升級、跨平臺性好、用戶使用方便等。

CS即“Client/Server”(客戶端/服務器模式) :這種模式下,需要在客戶端安裝專門的應用程序來與服務器進行交互??蛻舳撕头掌鞫硕汲袚欢ǖ臉I(yè)務邏輯和數(shù)據(jù)處理任務。例如一些大型的游戲客戶端、企業(yè)級的本地應用程序等。其優(yōu)點可能包括響應速度快、能充分利用本地資源等,但缺點是部署和維護成本較高,客戶端的更新較為復雜。

比如在線購物網(wǎng)站通常采用 BS 架構,用戶通過瀏覽器就能訪問和操作;而像一些專業(yè)的圖形設計軟件可能采用 CS 架構,以充分發(fā)揮本地計算機的性能。

 二.API測試工具

API 測試工具是專門用于對應用程序編程接口(API)進行測試和驗證的軟件工具。

這些工具的主要目的是幫助開發(fā)人員、測試人員和質量保證團隊確保 API 按照預期工作,能夠正確處理輸入請求并返回準確、有效的響應。

ApiPost

優(yōu)勢:

  • 提供中文界面,對國內用戶更友好。
  • 接口文檔生成和分享功能便捷,適合團隊協(xié)作。
  • 支持離線使用,適用于特殊網(wǎng)絡環(huán)境。

適用場景:

  • 適合國內開發(fā)團隊,尤其是需要高效協(xié)作和生成詳細接口文檔的項目。

ApiFox

  • 優(yōu)勢:
    • 集 API 文檔、調試、Mock、自動化測試為一體。
    • 支持多種數(shù)據(jù)格式的導入和導出。
  • 適用場景:
    • 適用于需要全面管理 API 全生命周期的項目。

Postman

  • 優(yōu)勢:
    • 應用廣泛,社區(qū)活躍,資源豐富。
    • 支持豐富的插件擴展。
  • 適用場景:
    • 適合個人開發(fā)者和大型國際化團隊。

雖然Postman很好,但是不用魔法我打不開,注冊登錄不了(囧)

所以我選擇Apipost

三.請求

1.簡單參數(shù)

簡單參數(shù)通常指的是基本數(shù)據(jù)類型的參數(shù),例如整數(shù)、浮點數(shù)、布爾值、字符或字符串等。這些參數(shù)通常是獨立的值,直接傳遞給函數(shù)或方法進行處理。

(1)原始方式(不推薦)

通過 HttpServletRequest 對象來獲取原始的請求信息

get方式 

package com.example.demos.controllers;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class RequestController {
    //原始方式
    @RequestMapping("/simpleParam")
    public String simpleParam(HttpServletRequest request){
        //獲取請求參數(shù)
        String name=request.getParameter("name");
        String ageStr = request.getParameter("age");
        int age=Integer.parseInt(ageStr);//類型轉換
        System.out.println(name+":"+age);
        return "ok";
    }
}

因為繁瑣,且要手動進行類型轉換,所以一般不用 

(2)Spring Boot方式

將請求參數(shù)名寫在形參列表里,spring boot會自動轉換類型

@RequestMapping("/simpleParam")
    public String simpleParam(String name,Integer age){
        System.out.println(name+":"+age);
        return "ok";

若是post方式,要將參數(shù)的值寫在 body里,如下

若方法形參和請求參數(shù)名不一致,不會報錯,會接收到空(NULL)

可以使用映射使兩個名對應上

@RequestParam 是 Spring 框架中用于處理 HTTP 請求參數(shù)的注解。

當在控制器的方法參數(shù)上使用 @RequestParam 時,可以將請求中的參數(shù)值綁定到方法的參數(shù)上。

@RequestParam 還可以設置一些屬性,例如:

  • required:指定參數(shù)是否必需,默認值為 true。如果設置為 false,當請求中沒有該參數(shù)時,不會拋出異常。
  • defaultValue:當請求中沒有該參數(shù)時使用的默認值。

2 .實體參數(shù)

實體參數(shù)通常指的是一個具有復雜結構或包含多個相關屬性的對象或數(shù)據(jù)結構

POJO(Plain Old Java Object)即普通的 Java 對象 。

{
與 POJO(Plain Old Java Object,普通 Java 對象)相對應的概念包括

DTO(Data Transfer Object,數(shù)據(jù)傳輸對象):主要用于在不同層或不同系統(tǒng)之間傳輸數(shù)據(jù),通常只包含必要的數(shù)據(jù)字段,并且這些字段通常是只讀的。VO(Value Object,值對象):用于表示不可變的值,通常只包含屬性和訪問這些屬性的方法,并且沒有任何行為邏輯。Entity(實體):在數(shù)據(jù)庫相關的設計中,用于表示數(shù)據(jù)庫中的表對應的對象,通常與數(shù)據(jù)庫中的記錄相對應,并包含持久化相關的邏輯。

 }

POJO user對象

package com.example.demos.pojos;
public class User {
    private String name;
    private Integer age;
    public User() {
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
    /**
     * 設置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 獲取
     * @return age
     */
    public Integer getAge() {
        return age;
    }
    /**
     * 設置
     * @param age
     */
    public void setAge(Integer age) {
        this.age = age;
    }
    public String toString() {
        return "User{name = " + name + ", age = " + age + "}";
    }
}
@RequestMapping("/simpleParam")
    public String simpleParam(User user){
        System.out.println(user.getAge()+ user.getName());
        return "ok";
    }

復雜實體類型

3.數(shù)組集合參數(shù)

 4.日期參數(shù)

 5.JSON參數(shù)

@RequestBody 是 Spring 框架中用于處理 HTTP 請求體的注解。

當在控制器的方法參數(shù)上使用 @RequestBody 時,它會將 HTTP 請求體中的數(shù)據(jù)(通常是 JSON、XML 等格式)綁定到方法的參數(shù)對象上。

他還有將數(shù)據(jù)響應給瀏覽器的功能,見后文

 6.路徑參數(shù)

路徑參數(shù)是在 URL 路徑中傳遞的參數(shù)。

例如,假設有一個 URL 類似于 https://example.com/user/123 ,其中的 123 就是一個路徑參數(shù)。在 Web 開發(fā)中,服務器端可以獲取這個路徑參數(shù),并根據(jù)其值進行相應的處理。

多個路徑參數(shù)樣式 

路徑參數(shù)具有以下重要作用

  • 精確資源定位
  • 能夠準確地指定要訪問的特定資源。例如,在一個博客系統(tǒng)中,/post/123 中的 123 可以準確指向特定的文章。
  • 簡化 URL 結構
  • 使 URL 看起來更簡潔和有組織,而不是通過大量的查詢參數(shù)來傳遞關鍵信息。
  • 提高路由效率
  • 服務器端可以基于路徑參數(shù)快速進行路由決策,提高請求處理的效率。
  • 增強用戶體驗
  • 對于用戶來說,直觀的路徑參數(shù)更容易理解和記憶。
  • 實現(xiàn)動態(tài)內容展示
  • 根據(jù)不同的路徑參數(shù),服務器可以動態(tài)地生成和返回不同的內容。
  • 便于權限控制和訪問管理
  • 可以基于路徑參數(shù)來設置不同的權限規(guī)則,控制對特定資源的訪問。

例如,在一個在線教育平臺,/course/101/lesson/5 這樣的路徑參數(shù)能夠清晰地標識特定的課程和課程中的特定章節(jié),服務器可以據(jù)此提供準確的教學內容,并進行相應的權限驗證。

總結

四.響應

1.響應的實現(xiàn)和過程

基本上都使用 @RestController 注解的控s制器方法來直接返回數(shù)據(jù) 

如下圖,@RestController 注解包含了 @ResponseBody注解

響應的數(shù)據(jù)若是較復雜,以JSON格式傳遞

但這樣還是不方便管理和后期維護,所以有統(tǒng)一的響應格式

2.統(tǒng)一響應格式 

常見的統(tǒng)一響應格式可以包含以下幾個部分:

  • status :表示請求的處理狀態(tài),通常是一個整數(shù),例如1表示成功,0表示失敗。
  • message :對狀態(tài)的簡要描述信息,解釋請求處理的結果。
  • data :實際要返回的數(shù)據(jù)內容,可以是對象、數(shù)組、字符串等各種數(shù)據(jù)類型。

Result封裝類代碼 

/**
 * 統(tǒng)一響應結果封裝類
 */
public class Result {
    private Integer code ;//1 成功 , 0 失敗
    private String msg; //提示信息
    private Object data; //數(shù)據(jù) data
    public Result() {
    }
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static Result success(Object data){
        return new Result(1, "success", data);
    }
    public static Result success(){
        return new Result(1, "success", null);
    }
    public static Result error(String msg){
        return new Result(0, msg, null);
    }
    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

封裝了類的靜態(tài)方法可以直接調用封裝的success()方法 快速返回數(shù)據(jù)。

例如

 @RequestMapping("/simpleParam")
    public Result simpleParam(User user){
        System.out.println(user.getAge()+ user.getName());
        //return new Result(1,"success","Hello");
         return Result.success("Hello,SpringBoot");
    }

瀏覽器接收到的JSON數(shù)據(jù):

五.樣例案例  TIP DOM4J 

DOM4J 是一個 Java 的 XML 操作庫。

它具有以下特點和優(yōu)勢:

  • 強大的解析功能:能夠有效地解析和處理復雜的 XML 文檔。
  • 靈活的操作:支持對 XML 節(jié)點的創(chuàng)建、修改、刪除、查詢等操作。
  • 易于使用:提供了簡潔直觀的 API,使得開發(fā)人員能夠輕松上手。

作用:

  • XML 文檔解析

能夠讀取和解析 XML 文檔,將其轉換為易于操作的 Java 對象結構。

  • 節(jié)點操作

可以方便地訪問、添加、修改和刪除 XML 文檔中的節(jié)點(元素、屬性、文本等)。

  • 數(shù)據(jù)提取

從 XML 文檔中提取所需的數(shù)據(jù),例如特定元素的值或屬性的值。

  • 構建 XML 文檔

能夠從零開始創(chuàng)建新的 XML 文檔,并按照指定的結構添加內容。

  • 遍歷文檔

支持對 XML 文檔進行深度優(yōu)先或廣度優(yōu)先的遍歷,以便處理文檔中的各個部分。

  • 與其他系統(tǒng)集成

在需要與基于 XML 的外部系統(tǒng)進行數(shù)據(jù)交互時,DOM4J 可以幫助進行數(shù)據(jù)的轉換和處理。 案例 

  •  實現(xiàn)聯(lián)系前后端展示一個頁面

相關數(shù)據(jù)存儲在 一個XML文件里面

因為要使用DOMJ4解析XML對象,導入DOMJ4依賴

相關目錄說明

相關代碼 

(1)本項目構建的XML解析工具類

package com.example.springbootwebpractice.utils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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<T>(); //封裝解析出來的數(shù)據(jù)
        try {
            //1.獲取一個解析器對象
            SAXReader saxReader = new SAXReader();
            //2.利用解析器把xml文件加載到內存中,并返回一個文檔對象
            Document document = saxReader.read(new File(file));
            //3.獲取到根標簽
            Element rootElement = document.getRootElement();
            //4.通過根標簽來獲取 user 標簽
            List<Element> elements = rootElement.elements("emp");
            //5.遍歷集合,得到每一個 user 標簽
            for (Element element : elements) {
                //獲取 name 屬性
                String name = element.element("name").getText();
                //獲取 age 屬性
                String age = element.element("age").getText();
                //獲取 image 屬性
                String image = element.element("image").getText();
                //獲取 gender 屬性
                String gender = element.element("gender").getText();
                //獲取 job 屬性
                String job = element.element("job").getText();
                //組裝數(shù)據(jù)
                Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, Integer.class, String.class, String.class, String.class);
                constructor.setAccessible(true);
                T object = constructor.newInstance(name, Integer.parseInt(age), image, gender, job);
                list.add(object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}

(2) Emp pojo對象

package com.example.springbootwebpractice.pojo;
public class Emp {
    private String name;
    private Integer age;
    private String image;
    private String gender;
    private String job;
    public Emp() {
    }
    public Emp(String name, Integer age, String image, String gender, String job) {
        this.name = name;
        this.age = age;
        this.image = image;
        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 getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    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;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", image='" + image + '\'' +
                ", gender='" + gender + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

(3)EmpController 控制器

package com.example.springbootwebpractice.controller;
import com.example.springbootwebpractice.pojo.Emp;
import com.example.springbootwebpractice.pojo.Result;
import com.example.springbootwebpractice.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Iterator;
import java.util.List;
@RestController
public class EmpController {
    @RequestMapping("/listEmp")
    public Result list(){
        //1.加載并解析xml文件
        String file=this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> emplist=XmlParserUtils.parse(file,Emp.class);
        //2.對數(shù)據(jù)進行轉換處理-gender,job
        Iterator<Emp> it=emplist.iterator();
        while (it.hasNext()){
            String gender = it.next().getGender();
            if(gender.equals("1")){
                it.next().setGender("男");
            }else if(gender.equals("2")){
                it.next().setGender("女");
            }
            String job= it.next().getJob();
            if(job.equals("1")){
                it.next().setJob("講師");
            }else if(job.equals("2")){
                it.next().setJob("班主任");
            }else if(job.equals("3")){
                it.next().setJob("就業(yè)指導");
            }
        }
        //3.響應數(shù)據(jù)
        return Result.success(emplist);
    }
}

 (4)統(tǒng)一響應格式 Result

代碼見上文

(5)部分前端代碼

基于Vue框架和Axious

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>員工信息</title>
</head>
<link rel="stylesheet" href="element-ui/index.css" rel="external nofollow" >
<script src="./js/vue.js"></script>
<script src="./element-ui/index.js"></script>
<script src="./js/axios-0.18.0.js"></script>
<body>
    <h1 align="center">員工信息列表展示</h1>
    <div id="app">
        <el-table :data="tableData" style="width: 100%"  stripe border >
            <el-table-column prop="name" label="姓名" align="center" min-width="20%"></el-table-column>
            <el-table-column prop="age" label="年齡" align="center" min-width="20%"></el-table-column>
            <el-table-column label="圖像" align="center"  min-width="20%">
                <template slot-scope="scope">
                    <el-image :src="scope.row.image" style="width: 80px; height: 50px;"></el-image>
                </template>
            </el-table-column>
            <el-table-column prop="gender" label="性別" align="center"  min-width="20%"></el-table-column>
            <el-table-column prop="job" label="職位" align="center"  min-width="20%"></el-table-column>
        </el-table>
    </div>
</body>
<style>
    .el-table .warning-row {
        background: oldlace;
    }
    .el-table .success-row {
        background: #f0f9eb;
    }
</style>
<script>
    new Vue({
        el: "#app",
        data() {
            return {
                tableData: []
            }
        },
        mounted(){
            axios.get('/listEmp').then(res=>{
                if(res.data.code){
                    this.tableData = res.data.data;
                }
            });
        },
        methods: {
        }
    });
</script>
</html>

效果 

實時響應

前端頁面

注意:這里訪問的連接不是直接訪問后端,而是訪問前端,然后前通過axios異步訪問后端,后端再發(fā)送給前端,隨即渲染展示到頁面 

瀏覽器先向服務器請求頁面emp.html,掛載時頁面的鉤子方法mounted根據(jù)數(shù)據(jù)地址/listEmp自動向服務器申請數(shù)據(jù)

六.分層解耦引入和概述

目前所有代碼寫在一個控制器里

復用性差,難以維護 

單一職責原則

單一職責原則(Single Responsibility Principle,簡稱 SRP) 是面向對象編程中的一個重要原則。它指出:一個類應該只有一個引起它變化的原因。

這意味著一個類應該專注于完成一項特定的任務或職責,而不應該承擔過多不同類型的職責。

例如,假設有一個 Employee 類,如果它既負責員工的基本信息管理(如姓名、工號等),又負責計算員工的工資和績效,那么就違反了單一職責原則。

更好的做法是將員工信息管理和工資績效計算分別放在不同的類中,比如 EmployeeInfo 類和 EmployeeSalaryCalculator 類。

三層架構

三層架構通常包括表現(xiàn)層(Presentation Layer)、業(yè)務邏輯層(Business Logic Layer)和數(shù)據(jù)訪問層(Data Access Layer)。

表現(xiàn)層

  • 通常由 JSP、Servlet、Thymeleaf 模板等技術實現(xiàn)。
  • 負責接收用戶的請求,并將處理結果以網(wǎng)頁、JSON 數(shù)據(jù)等形式返回給用戶。(控制層,請求和響應)
  • 例如,用戶在網(wǎng)頁上提交表單,表現(xiàn)層會獲取這些表單數(shù)據(jù),并將其傳遞給業(yè)務邏輯層進行處理。

業(yè)務邏輯層(service)

  • 由一系列的 Java 類組成,處理具體的業(yè)務邏輯。
  • 例如,在一個電商系統(tǒng)中,訂單的生成、庫存的扣減、用戶積分的計算等業(yè)務邏輯都在這一層實現(xiàn)。
  • 業(yè)務邏輯層接收表現(xiàn)層傳來的數(shù)據(jù),進行處理后,再調用數(shù)據(jù)訪問層獲取或更新數(shù)據(jù)。

數(shù)據(jù)訪問層(dao)

負責與數(shù)據(jù)庫進行交互,執(zhí)行數(shù)據(jù)的增刪改查操作。通常使用 JDBC、MyBatis、Hibernate 等技術來實現(xiàn)。數(shù)據(jù)訪問層將數(shù)據(jù)庫中的數(shù)據(jù)提取出來,轉化為業(yè)務邏輯層能夠處理的對象,或者將業(yè)務邏輯層處理后的數(shù)據(jù)保存到數(shù)據(jù)庫中。

(1)數(shù)據(jù)訪問層(Dao)

在編寫 DAO(Data Access Object,數(shù)據(jù)訪問對象)包程序時要面向接口編程:

1.解耦和靈活性:通過定義接口,可以將數(shù)據(jù)訪問的具體實現(xiàn)與使用數(shù)據(jù)訪問的其他部分代碼解耦。這意味著如果需要更改數(shù)據(jù)存儲方式(例如從數(shù)據(jù)庫切換到文件存儲或云存儲),只需要更改實現(xiàn)接口的具體類,而無需修改使用數(shù)據(jù)訪問的業(yè)務邏輯代碼。
例如,如果最初使用的是關系型數(shù)據(jù)庫的 DAO 實現(xiàn),后來需要切換為 NoSQL 數(shù)據(jù)庫,只需創(chuàng)建新的符合接口的 NoSQL 實現(xiàn)類,而調用方代碼無需更改。

2.代碼的可維護性:接口定義了明確的方法簽名和功能規(guī)范,使得其他開發(fā)者能夠清晰地了解 DAO 應該提供的功能。這有助于提高代碼的可讀性和可理解性,從而更易于維護。
比如,新的開發(fā)者加入項目,通過查看接口就能快速了解數(shù)據(jù)訪問的相關操作。

3.支持多態(tài)和依賴注入:在使用依賴注入框架(如 Spring)時,可以方便地注入不同的 DAO 實現(xiàn)類。這使得代碼更具靈活性和可測試性。
例如,在測試時可以注入一個模擬的 DAO 實現(xiàn)類,而在生產(chǎn)環(huán)境中注入實際的數(shù)據(jù)庫操作的 DAO 實現(xiàn)類。

4.便于團隊協(xié)作:不同的開發(fā)者可以同時工作在接口的實現(xiàn)和使用接口的代碼上,提高開發(fā)效率。
假設一個團隊中,一部分人負責實現(xiàn) DAO 接口,另一部分人負責編寫業(yè)務邏輯使用這些接口,兩者可以并行開發(fā),互不干擾。

5.提高代碼的可擴展性:當需要添加新的功能或方法時,只需在接口中添加,然后在具體的實現(xiàn)類中進行實現(xiàn),而不會影響到現(xiàn)有的使用代碼。
比如,最初的接口只有查詢方法,后來需要添加插入和更新方法,只需在接口中添加,然后在實現(xiàn)類中實現(xiàn)即可。

案例優(yōu)化-Dao層:

接口:

package com.example.springbootwebpractice.dao;
import com.example.springbootwebpractice.pojo.Emp;
import java.util.List;
public interface EmpDao {
    List<Emp> listEmp();
}

實現(xiàn)類:

package com.example.springbootwebpractice.dao.impl;
import com.example.springbootwebpractice.dao.EmpDao;
import com.example.springbootwebpractice.pojo.Emp;
import com.example.springbootwebpractice.utils.XmlParserUtils;
import java.util.List;
public class EmpDaoA implements EmpDao {
    @Override
    public List<Emp> listEmp() {
        String file=this.getClass().getClassLoader().getResource("emp.xml").getFile();
        //System.out.println(file);
        List<Emp> emplist= XmlParserUtils.parse(file,Emp.class);
        return emplist;
    }
}

簡單來說,就是先創(chuàng)建一個接口限制一下方法名和返回結果,具體是從數(shù)據(jù)庫還是文件取從具體繼承的類中實現(xiàn)

(2)業(yè)務邏輯層(service)

案例優(yōu)化-service層

接口:

package com.example.springbootwebpractice.service;
import com.example.springbootwebpractice.pojo.Emp;
import java.util.List;
public interface EmpService {
   List<Emp> listEmp();
}

具體實現(xiàn)類:

package com.example.springbootwebpractice.service.impl;
import com.example.springbootwebpractice.dao.EmpDao;
import com.example.springbootwebpractice.dao.impl.EmpDaoA;
import com.example.springbootwebpractice.pojo.Emp;
import com.example.springbootwebpractice.service.EmpService;
import java.util.Iterator;
import java.util.List;
public class EmpServiceA implements EmpService {
    private EmpDao empDao=new EmpDaoA();
    //面向接口定義對象
    @Override
    public List<Emp> listEmp() {
        //1.調用Dao獲取數(shù)據(jù)
        List<Emp> emplist=empDao.listEmp();
        //2.數(shù)據(jù)處理
        Iterator<Emp> it=emplist.iterator();
        while (it.hasNext()){
            String gender = it.next().getGender();
            if(gender.equals("1")){
                it.next().setGender("男");
            }else if(gender.equals("2")){
                it.next().setGender("女");
            }
            String job= it.next().getJob();
            if(job.equals("1")){
                it.next().setJob("講師");
            }else if(job.equals("2")){
                it.next().setJob("班主任");
            }else if(job.equals("3")){
                it.next().setJob("就業(yè)指導");
            }
        }
        return emplist;
    }
}

(3)表現(xiàn)層

案例優(yōu)化-controller層

package com.example.springbootwebpractice.controller;
import com.example.springbootwebpractice.pojo.Emp;
import com.example.springbootwebpractice.pojo.Result;
import com.example.springbootwebpractice.service.EmpService;
import com.example.springbootwebpractice.service.impl.EmpServiceA;
import com.example.springbootwebpractice.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Iterator;
import java.util.List;
@RestController
public class EmpController {
    private EmpService empService=new EmpServiceA();
    @RequestMapping("/listEmp")
    public Result list(){
        List<Emp> emplist=empService.listEmp();
        //3.響應數(shù)據(jù)
        return Result.success(emplist);
    }
}

分層處理流程圖 

                 之所以要分層,是為了在編碼時專注于某一件事情,維護起來更加簡單 

分層解耦 

1.耦合和內聚

耦合(Coupling)和內聚(Cohesion) 是軟件工程中用于評估軟件模塊設計質量的兩個重要概念。

耦合 指的是不同模塊之間相互依賴的程度。

耦合程度低意味著模塊之間的依賴關系較少、較弱,一個模塊的修改對其他模塊的影響較小。

例如,如果模塊 A 直接操作模塊 B 內部的數(shù)據(jù),這就是強耦合。但如果模塊 A 只是通過模塊 B 提供的明確接口進行交互,這就是弱耦合。

耦合的類型包括:

  • 內容耦合:一個模塊直接訪問另一個模塊的內部數(shù)據(jù)或代碼。這是最強且最不好的耦合形式。
  • 公共耦合:多個模塊都訪問同一個全局數(shù)據(jù)環(huán)境。
  • 控制耦合:一個模塊通過傳遞控制信息(如標志、開關量等)來影響另一個模塊的功能。
  • 標記耦合:兩個模塊之間通過參數(shù)傳遞復雜的數(shù)據(jù)結構。
  • 數(shù)據(jù)耦合:模塊之間通過參數(shù)傳遞基本數(shù)據(jù)類型的數(shù)據(jù)。這是理想的、低耦合的方式。

內聚 則衡量的是一個模塊內部各個元素之間的關聯(lián)程度。

內聚程度高表示模塊內部的元素緊密相關,共同完成一個明確、單一的功能。

例如,一個專門負責處理學生成績計算的模塊,如果它只包含與成績計算相關的代碼和數(shù)據(jù),就是高內聚。

低內聚的情況比如一個模塊既處理成績計算,又處理學生的課程安排,功能過于混雜。

在軟件設計中,我們通常希望達到低耦合高內聚的目標。

舉例來說,假設有一個學生管理系統(tǒng),其中有學生信息模塊、課程模塊和成績模塊。

  • 如果學生信息模塊直接修改成績模塊的數(shù)據(jù),這就是強耦合,不利于系統(tǒng)的維護和擴展。
  • 而如果每個模塊都專注于自己明確的功能,如學生信息模塊只負責管理學生的基本信息,成績模塊只負責成績的相關操作,這就是高內聚,使得每個模塊的功能清晰、單一,易于理解和維護。

低耦合和高內聚的設計可以提高軟件的可維護性、可擴展性和可重用性,降低軟件開發(fā)和維護的成本。

控制反轉 是一種設計原則,它指的是控制權從應用程序代碼轉移到了外部的框架或容器。在傳統(tǒng)的編程中,對象的創(chuàng)建和依賴關系的管理通常由應用程序自身負責。而在控制反轉的理念下,這些控制權被交給了外部的機制。

依賴注入 是實現(xiàn)控制反轉的一種方式。它是指在一個對象創(chuàng)建或使用的時候,將其依賴的對象通過外部的方式(例如構造函數(shù)、屬性設置方法等)傳遞給它,而不是由對象自身去創(chuàng)建或獲取這些依賴。

IOC&DI入門

@Component 注解用于將一個普通的 Java 類標記為一個 Spring 管理的組件。這意味著 Spring 容器會負責創(chuàng)建該類的實例(bean),并對其進行管理(包括依賴注入等)。

@Autowired 是 Spring 框架中的一個注解,用于實現(xiàn)依賴自動注入。

當一個類中的成員變量、方法參數(shù)或構造函數(shù)參數(shù)被標注為 @Autowired 時,Spring 框架會自動查找匹配類型的 bean,并將其注入到該成員變量、方法參數(shù)或構造函數(shù)參數(shù)中。

 七.IOC詳解

respon除了 @Component 注解,通常還有一些具有更具體語義的衍生注解,如:

  • @Service:用于標注服務層的組件。
  • @Repository:用于標注數(shù)據(jù)訪問層(DAO 層)的組件。
  • @Controller:用于標注控制層(MVC 架構中的控制器)的組件。

這些注解的功能與 @Component 類似,但更明確地表明了組件的類型和用途,有助于提高代碼的可讀性和可維護性。

 @Controller也可以不用添加,前面講過,@RestController注解里包含了這個注解

可以在注解后面加(“name”)用來給此bean對象署名

默認類名首字母小寫

組件掃描

八.DI詳解

 如果多個繼承相同接口的bean對象都依賴注入的話,會發(fā)生矛盾

用以下方式,處理矛盾

@Primary注解

@Primary 是 Spring 框架中的注解。當在多個相同類型的 bean 被定義時,使用 @Primary 注解可以指定其中一個作為首要的(primary)bean。當 Spring 進行依賴注入并且需要選擇一個該類型的 bean 時,如果存在被標注為 @Primary 的 bean,就會優(yōu)先選擇它。

@Qualifier注解 

“Qualifier”常見中文釋義為“限定詞;合格者;修飾語”。

@Qualifier 是 Spring 框架中的注解。用于在存在多個相同類型的 bean 時,更精確地指定要注入的 bean。

當多個 bean 屬于同一類型,而僅使用 @Autowired 無法明確要注入哪一個時,可以結合 @Qualifier 注解,通過指定 bean 的名稱來明確注入的對象。

@Resource注解 

@Resource是 Java 中用于依賴注入的注解。

它默認按照名稱進行依賴注入,如果找不到與名稱匹配的 bean,則按照類型進行匹配注入。

到此這篇關于SpringBoot Web開發(fā)(請求,響應,分層解耦)的文章就介紹到這了,更多相關SpringBoot Web請求響應內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java多線程入門知識及示例程序

    java多線程入門知識及示例程序

    java多線程入門知識及示例程序,大家參考使用吧
    2013-12-12
  • Spring 事件監(jiān)聽機制實現(xiàn)跨模塊調用的思路詳解

    Spring 事件監(jiān)聽機制實現(xiàn)跨模塊調用的思路詳解

    之前一個項目,有兩個模塊,A 模塊需要依賴 B 模塊,但現(xiàn)在 B 模塊有地方需要調用 A 模塊的方法,如果直接依賴,又會產(chǎn)生循環(huán)依賴問題,最終選擇使用 spring 的事件監(jiān)聽來解決該問題,下面給大家介紹Spring 事件監(jiān)聽機制實現(xiàn)跨模塊調用的思路,感興趣的朋友一起看看吧
    2024-05-05
  • Springboot mybatis常見配置問題解決

    Springboot mybatis常見配置問題解決

    這篇文章主要介紹了Springboot mybatis常見配置問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • idea自帶database連接mysql失敗問題的解決辦法

    idea自帶database連接mysql失敗問題的解決辦法

    在IDEA?帶的數(shù)據(jù)庫連接?具中,可以連接MySQL數(shù)據(jù)庫,但是有的時候連接出現(xiàn)錯誤,連接不上數(shù)據(jù)庫,下面這篇文章主要給大家介紹了關于idea自帶database連接mysql失敗問題的解決辦法,需要的朋友可以參考下
    2023-06-06
  • java 驗證碼的生成實現(xiàn)

    java 驗證碼的生成實現(xiàn)

    這篇文章主要介紹了java 驗證碼的生成實現(xiàn)的相關資料,需要的朋友可以參考下
    2017-08-08
  • SpringBoot整合Shiro的方法詳解

    SpringBoot整合Shiro的方法詳解

    Apache?Shiro是一個java安全(權限)框架,Shiro可以非常容易的開發(fā)出足夠好的應用,其不僅可以用在javase環(huán)境,也可以用在javaee環(huán)境。本文介紹了SpringBoot整合Shiro的方法,需要的可以參考一下
    2022-05-05
  • java虛擬機多線程進階篇總結

    java虛擬機多線程進階篇總結

    在本篇內容里小編給大家整理了關于java虛擬機多線程進階篇的相關知識點內容,有興趣的朋友們跟著參考下。
    2019-06-06
  • SpringBoot實現(xiàn)導出復雜對象到Excel文件

    SpringBoot實現(xiàn)導出復雜對象到Excel文件

    這篇文章主要為大家詳細介紹了如何使用Hutool和EasyExcel兩種方式來實現(xiàn)在Spring Boot項目中導出復雜對象到Excel文件,需要的小伙伴可以參考下
    2025-03-03
  • 以Spring Boot的方式顯示圖片或下載文件到瀏覽器的示例代碼

    以Spring Boot的方式顯示圖片或下載文件到瀏覽器的示例代碼

    這篇文章主要介紹了以Spring Boot的方式顯示圖片或下載文件到瀏覽器的示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • java工具類SendEmailUtil實現(xiàn)發(fā)送郵件

    java工具類SendEmailUtil實現(xiàn)發(fā)送郵件

    這篇文章主要為大家詳細介紹了java工具類SendEmailUtil實現(xiàn)發(fā)送郵件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02

最新評論