SpringBoot Web开发(请求,响应,分层解耦)

news2024/12/29 11:16:38

Author_T17🥥

目录

一.请求响应概述

1.Servlet

2.DispatcherServlet

3.请求响应工作概图

 4.BS/CS架构

 二.API测试工具

三.请求

1.简单参数

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

​编辑

(2)Spring Boot方式

2 .实体参数

3.数组集合参数

 4.日期参数​

 5.JSON参数

 6.路径参数

总结​

四.响应

1.响应的实现和过程

2.统一响应格式 

五.样例案例 

TIP

DOM4J 

案例 

相关代码 

(1)本项目构建的XML解析工具类

(2) Emp pojo对象

(3)EmpController 控制器

 (4)统一响应格式 Result

(5)部分前端代码

效果 

六.分层解耦引入和概述

单一职责原则

三层架构​编辑

分层解耦 

 七.IOC详解

八.DI详解


一.请求响应概述

1.Servlet

在 Spring Boot 的 Web 请求响应处理中,Servlet 起着关键的作用。

Servlet 是 Java Web 开发中的基本组件,主要负责处理客户端的请求并生成响应。

具体来说,它具有以下重要作用:

  1. 接收请求:Servlet 能够接收来自客户端(如浏览器)发送的 HTTP 请求。
  2. 处理请求:在接收到请求后,执行相应的业务逻辑处理。这可能包括与数据库交互、进行数据计算、验证用户输入等操作。
  3. 控制流程:根据请求的类型和参数,决定后续的处理流程和响应方式。
  4. 生成响应:处理完请求后,生成要返回给客户端的响应数据。
  5. 与其他组件协作:可以与其他的 Java 类、服务或组件进行协作,以完成复杂的业务功能。

例如,在一个用户登录的场景中,Servlet 接收到用户提交的登录表单数据,然后验证用户名和密码是否正确。如果正确,生成一个成功登录的响应;如果不正确,生成一个错误提示的响应。

在 Spring Boot 中,DispatcherServlet 是一个特殊的 Servlet,它负责协调和分发请求到具体的控制器(Controller)进行处理,使得整个请求处理流程更加清晰和高效。

Dispatcher:调度员;调度程序;发送器

2.DispatcherServlet

在 Spring Boot 中,DispatcherServlet 是一个核心组件,起着非常重要的作用

DispatcherServlet 主要负责接收客户端的请求,并将请求分发给相应的处理器(Handler)进行处理。它是 Spring Web MVC 框架的前端控制器。

其工作流程大致如下:

当客户端发送一个 HTTP 请求到应用程序时,DispatcherServlet 首先会接收到这个请求。然后,它会根据请求的 URL 和其他相关信息,通过一系列的映射规则,来确定应该调用哪个控制器(Controller)来处理这个请求。

在确定了控制器之后,DispatcherServlet 会将请求传递给对应的控制器方法进行处理。控制器处理完请求后,通常会返回一个模型(Model)和视图(View)的信息。

DispatcherServlet 接着会根据返回的视图信息,选择合适的视图解析器(View Resolver)来将模型数据渲染成最终的响应页面,并将响应返回给客户端。

例如,如果有一个用户请求获取商品列表的页面,DispatcherServlet 会找到处理该请求的商品控制器,然后由控制器获取商品数据并返回给 DispatcherServlet ,DispatcherServlet 再通过视图解析器将数据展示在相应的页面上。

总之,DispatcherServlet 是 Spring Boot 中实现 Web 应用请求处理和响应生成的关键环节,确保了整个 Web 应用的流畅运行和高效响应。

 DispatcherServlet 类继承了Servlet 接口

3.请求响应工作概图

 DispatcherServlet会根据请求调度Controller控制器,然后获得响应的数据

(1)HttpServletRequest 是 Java Servlet 规范中定义的一个接口,用于表示客户端发送到服务器的 HTTP 请求。

它包含了大量与请求相关的信息和方法:

请求方法:例如 GETPOSTPUTDELETE 等,通过 getMethod() 方法获取。
请求 URL:可以通过 getRequestURI() 方法获取请求的资源路径,getQueryString() 方法获取查询字符串。
请求头信息:如 User-Agent(客户端浏览器和操作系统信息)、Content-Type(请求体的数据类型)等,使用 getHeader(String name) 方法获取指定的请求头。
请求参数:包括表单提交的参数、URL 中的参数等,通过 getParameter(String name) 方法获取单个参数值,getParameterValues(String name) 方法获取具有多个值的参数。

例如,在一个登录页面中,用户输入用户名和密码后提交表单,服务器端可以通过 HttpServletRequest 来获取用户名和密码的参数值,进行后续的验证处理。

另外,如果客户端发送了一个带有特定 Cookie 的请求,服务器可以通过 getCookies() 方法获取这些 Cookie 信息,从而实现会话跟踪等功能。

HttpServletRequest 为服务器端处理客户端的 HTTP 请求提供了丰富的信息和操作方法,是构建 Web 应用的重要组成部分。

(2)HttpServletResponse 是 Java Servlet 规范中定义的一个接口,用于表示服务器对客户端 HTTP 请求的响应。

它包含了一系列方法,用于设置响应的状态码、响应头信息、响应体内容等。

一些常见的方法包括:

  1. setStatus(int status):设置响应的状态码,例如 200 表示成功,404 表示未找到资源,500 表示服务器内部错误等。
  2. setHeader(String name, String value):设置响应头信息,如设置 Content-Type 来指定响应体的数据类型。
  3. getWriter():获取一个 PrintWriter 对象,用于向响应体中写入字符数据。
  4. getOutputStream():获取一个 ServletOutputStream 对象,用于向响应体中写入二进制数据。

 4.BS/CS架构

BS即“Browser/Server”(浏览器/服务器模式) :在这种模式下,用户通过浏览器访问服务器上的应用程序。客户端主要负责显示数据和接收用户输入,而大部分的业务逻辑和数据处理都在服务器端完成。例如常见的各类网站、在线办公系统等。其优点包括易于维护和升级、跨平台性好、用户使用方便等。

CS即“Client/Server”(客户端/服务器模式) :这种模式下,需要在客户端安装专门的应用程序来与服务器进行交互。客户端和服务器端都承担一定的业务逻辑和数据处理任务。例如一些大型的游戏客户端、企业级的本地应用程序等。其优点可能包括响应速度快、能充分利用本地资源等,但缺点是部署和维护成本较高,客户端的更新较为复杂。

比如在线购物网站通常采用 BS 架构,用户通过浏览器就能访问和操作;而像一些专业的图形设计软件可能采用 CS 架构,以充分发挥本地计算机的性能。

 二.API测试工具

API 测试工具是专门用于对应用程序编程接口(API)进行测试和验证的软件工具

这些工具的主要目的是帮助开发人员、测试人员和质量保证团队确保 API 按照预期工作,能够正确处理输入请求并返回准确、有效的响应。

ApiPost

  • 优势:

    • 提供中文界面,对国内用户更友好。
    • 接口文档生成和分享功能便捷,适合团队协作。
    • 支持离线使用,适用于特殊网络环境。
  • 适用场景:

    • 适合国内开发团队,尤其是需要高效协作和生成详细接口文档的项目。

ApiFox

  • 优势:

    • 集 API 文档、调试、Mock、自动化测试为一体。
    • 支持多种数据格式的导入和导出。
  • 适用场景:

    • 适用于需要全面管理 API 全生命周期的项目。

Postman

  • 优势:

    • 应用广泛,社区活跃,资源丰富。
    • 支持丰富的插件扩展。
  • 适用场景:

    • 适合个人开发者和大型国际化团队。

虽然Postman很好,但是不用魔法我打不开,注册登录不了(囧)

所以我选择Apipost

三.请求

1.简单参数

简单参数通常指的是基本数据类型的参数,例如整数、浮点数、布尔值、字符或字符串等。这些参数通常是独立的值,直接传递给函数或方法进行处理。

(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){
        //获取请求参数

        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方式

将请求参数名写在形参列表里,spring boot会自动转换类型

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

若是post方式,要将参数的值写在 body里,如下

若方法形参和请求参数名不一致,不会报错,会接收到空(NULL)

可以使用映射使两个名对应上

@RequestParam 是 Spring 框架中用于处理 HTTP 请求参数的注解。

当在控制器的方法参数上使用 @RequestParam 时,可以将请求中的参数值绑定到方法的参数上。

@RequestParam 还可以设置一些属性,例如:

  • required:指定参数是否必需,默认值为 true。如果设置为 false,当请求中没有该参数时,不会抛出异常。
  • defaultValue:当请求中没有该参数时使用的默认值。

2 .实体参数

实体参数通常指的是一个具有复杂结构或包含多个相关属性的对象或数据结构

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

{
与 POJO(Plain Old Java Object,普通 Java 对象)相对应的概念包括

  1. DTO(Data Transfer Object,数据传输对象):主要用于在不同层或不同系统之间传输数据,通常只包含必要的数据字段,并且这些字段通常是只读的。
  2. VO(Value Object,值对象):用于表示不可变的值,通常只包含属性和访问这些属性的方法,并且没有任何行为逻辑。
  3. Entity(实体):在数据库相关的设计中,用于表示数据库中的表对应的对象,通常与数据库中的记录相对应,并包含持久化相关的逻辑。

 }

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.数组集合参数

 4.日期参数

 5.JSON参数

@RequestBody 是 Spring 框架中用于处理 HTTP 请求体的注解。

当在控制器的方法参数上使用 @RequestBody 时,它会将 HTTP 请求体中的数据(通常是 JSON、XML 等格式)绑定到方法的参数对象上。

他还有将数据响应给浏览器的功能,见后文

 6.路径参数

路径参数是在 URL 路径中传递的参数。

例如,假设有一个 URL 类似于 https://example.com/user/123 ,其中的 123 就是一个路径参数。在 Web 开发中,服务器端可以获取这个路径参数,并根据其值进行相应的处理。

多个路径参数样式 

路径参数具有以下重要作用

  1. 精确资源定位
    • 能够准确地指定要访问的特定资源。例如,在一个博客系统中,/post/123 中的 123 可以准确指向特定的文章。
  2. 简化 URL 结构
    • 使 URL 看起来更简洁和有组织,而不是通过大量的查询参数来传递关键信息。
  3. 提高路由效率
    • 服务器端可以基于路径参数快速进行路由决策,提高请求处理的效率。
  4. 增强用户体验
    • 对于用户来说,直观的路径参数更容易理解和记忆。
  5. 实现动态内容展示
    • 根据不同的路径参数,服务器可以动态地生成和返回不同的内容。
  6. 便于权限控制和访问管理
    • 可以基于路径参数来设置不同的权限规则,控制对特定资源的访问。

例如,在一个在线教育平台,/course/101/lesson/5 这样的路径参数能够清晰地标识特定的课程和课程中的特定章节,服务器可以据此提供准确的教学内容,并进行相应的权限验证。

总结

四.响应

1.响应的实现和过程

 

                         基本上都使用 @RestController 注解的控s制器方法来直接返回数据 

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

响应的数据若是较复杂,以JSON格式传递

但这样还是不方便管理和后期维护,所以有统一的响应格式

2.统一响应格式 

常见的统一响应格式可以包含以下几个部分:

  1. status :表示请求的处理状态,通常是一个整数,例如1表示成功,0表示失败。
  2. message :对状态的简要描述信息,解释请求处理的结果。
  3. data :实际要返回的数据内容,可以是对象、数组、字符串等各种数据类型。

Result封装类代码 

/**
 * 统一响应结果封装类
 */
public class Result {
    private Integer code ;//1 成功 , 0 失败
    private String msg; //提示信息
    private Object data; //数据 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 +
                '}';
    }
}

封装了类的静态方法可以直接调用封装的success()方法 快速返回数据。

例如

 @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数据:

五.样例案例 

TIP

DOM4J 

DOM4J 是一个 Java 的 XML 操作库

它具有以下特点和优势:

  1. 强大的解析功能:能够有效地解析和处理复杂的 XML 文档。
  2. 灵活的操作:支持对 XML 节点的创建、修改、删除、查询等操作。
  3. 易于使用:提供了简洁直观的 API,使得开发人员能够轻松上手。

作用:

  1. XML 文档解析

    能够读取和解析 XML 文档,将其转换为易于操作的 Java 对象结构。
  2. 节点操作

    可以方便地访问、添加、修改和删除 XML 文档中的节点(元素、属性、文本等)。
  3. 数据提取

    从 XML 文档中提取所需的数据,例如特定元素的值或属性的值。
  4. 构建 XML 文档

    能够从零开始创建新的 XML 文档,并按照指定的结构添加内容。
  5. 遍历文档

    支持对 XML 文档进行深度优先或广度优先的遍历,以便处理文档中的各个部分。
  6. 与其他系统集成

    在需要与基于 XML 的外部系统进行数据交互时,DOM4J 可以帮助进行数据的转换和处理。

案例 

 实现联系前后端展示一个页面

相关数据存储在 一个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>(); //封装解析出来的数据
        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();

                //组装数据
                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.对数据进行转换处理-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("就业指导");
            }
        }



        //3.响应数据
        return Result.success(emplist);
    }
}
 (4)统一响应格式 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">
<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异步访问后端,后端再发送给前端,随即渲染展示到页面 

浏览器先向服务器请求页面emp.html,挂载时页面的钩子方法mounted根据数据地址/listEmp自动向服务器申请数据

六.分层解耦引入和概述

目前所有代码写在一个控制器里

复用性差,难以维护 

单一职责原则

单一职责原则(Single Responsibility Principle,简称 SRP) 是面向对象编程中的一个重要原则。它指出:一个类应该只有一个引起它变化的原因。

这意味着一个类应该专注于完成一项特定的任务或职责,而不应该承担过多不同类型的职责

例如,假设有一个 Employee 类,如果它既负责员工的基本信息管理(如姓名、工号等),又负责计算员工的工资和绩效,那么就违反了单一职责原则。

更好的做法是将员工信息管理和工资绩效计算分别放在不同的类中,比如 EmployeeInfo 类和 EmployeeSalaryCalculator 类。

三层架构

三层架构通常包括表现层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer)。

表现层

  • 通常由 JSP、Servlet、Thymeleaf 模板等技术实现。
  • 负责接收用户的请求,并将处理结果以网页、JSON 数据等形式返回给用户。(控制层,请求和响应)
  • 例如,用户在网页上提交表单,表现层会获取这些表单数据,并将其传递给业务逻辑层进行处理。

业务逻辑层(service)

  • 由一系列的 Java 类组成,处理具体的业务逻辑。
  • 例如,在一个电商系统中,订单的生成、库存的扣减、用户积分的计算等业务逻辑都在这一层实现。
  • 业务逻辑层接收表现层传来的数据,进行处理后,再调用数据访问层获取或更新数据。

数据访问层(dao)

  • 负责与数据库进行交互,执行数据的增删改查操作。
  • 通常使用 JDBC、MyBatis、Hibernate 等技术来实现。
  • 数据访问层将数据库中的数据提取出来,转化为业务逻辑层能够处理的对象,或者将业务逻辑层处理后的数据保存到数据库中。

(1)数据访问层(Dao)

在编写 DAO(Data Access Object,数据访问对象)包程序时要面向接口编程

  1. 解耦和灵活性:通过定义接口,可以将数据访问的具体实现与使用数据访问的其他部分代码解耦。这意味着如果需要更改数据存储方式(例如从数据库切换到文件存储或云存储),只需要更改实现接口的具体类,而无需修改使用数据访问的业务逻辑代码。
    例如,如果最初使用的是关系型数据库的 DAO 实现,后来需要切换为 NoSQL 数据库,只需创建新的符合接口的 NoSQL 实现类,而调用方代码无需更改。

  2. 代码的可维护性:接口定义了明确的方法签名和功能规范,使得其他开发者能够清晰地了解 DAO 应该提供的功能。这有助于提高代码的可读性和可理解性,从而更易于维护。
    比如,新的开发者加入项目,通过查看接口就能快速了解数据访问的相关操作。

  3. 支持多态和依赖注入:在使用依赖注入框架(如 Spring)时,可以方便地注入不同的 DAO 实现类。这使得代码更具灵活性和可测试性。
    例如,在测试时可以注入一个模拟的 DAO 实现类,而在生产环境中注入实际的数据库操作的 DAO 实现类。

  4. 便于团队协作:不同的开发者可以同时工作在接口的实现和使用接口的代码上,提高开发效率。
    假设一个团队中,一部分人负责实现 DAO 接口,另一部分人负责编写业务逻辑使用这些接口,两者可以并行开发,互不干扰。

  5. 提高代码的可扩展性:当需要添加新的功能或方法时,只需在接口中添加,然后在具体的实现类中进行实现,而不会影响到现有的使用代码。
    比如,最初的接口只有查询方法,后来需要添加插入和更新方法,只需在接口中添加,然后在实现类中实现即可。

案例优化-Dao层:

接口:

package com.example.springbootwebpractice.dao;

import com.example.springbootwebpractice.pojo.Emp;

import java.util.List;

public interface EmpDao {
    List<Emp> listEmp();
}

实现类:

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;
    }
}

简单来说,就是先创建一个接口限制一下方法名和返回结果,具体是从数据库还是文件取从具体继承的类中实现

(2)业务逻辑层(service)

案例优化-service层

接口:

package com.example.springbootwebpractice.service;

import com.example.springbootwebpractice.pojo.Emp;

import java.util.List;

public interface EmpService {
   List<Emp> listEmp();
}

具体实现类:

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获取数据
        List<Emp> emplist=empDao.listEmp();
        //2.数据处理
        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("就业指导");
            }
        }

        return emplist;
    }
}

(3)表现层

案例优化-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.响应数据
        return Result.success(emplist);
    }
}

分层处理流程图 

                 之所以要分层,是为了在编码时专注于某一件事情,维护起来更加简单 

分层解耦 

1.耦合和内聚

耦合(Coupling)和内聚(Cohesion) 是软件工程中用于评估软件模块设计质量的两个重要概念。

耦合 指的是不同模块之间相互依赖的程度。

耦合程度低意味着模块之间的依赖关系较少、较弱,一个模块的修改对其他模块的影响较小。

例如,如果模块 A 直接操作模块 B 内部的数据,这就是强耦合。但如果模块 A 只是通过模块 B 提供的明确接口进行交互,这就是弱耦合。

耦合的类型包括:

  1. 内容耦合:一个模块直接访问另一个模块的内部数据或代码。这是最强且最不好的耦合形式。
  2. 公共耦合:多个模块都访问同一个全局数据环境。
  3. 控制耦合:一个模块通过传递控制信息(如标志、开关量等)来影响另一个模块的功能。
  4. 标记耦合:两个模块之间通过参数传递复杂的数据结构。
  5. 数据耦合:模块之间通过参数传递基本数据类型的数据。这是理想的、低耦合的方式。

内聚 则衡量的是一个模块内部各个元素之间的关联程度。

内聚程度高表示模块内部的元素紧密相关,共同完成一个明确、单一的功能。

例如,一个专门负责处理学生成绩计算的模块,如果它只包含与成绩计算相关的代码和数据,就是高内聚。

低内聚的情况比如一个模块既处理成绩计算,又处理学生的课程安排,功能过于混杂。

在软件设计中,我们通常希望达到低耦合高内聚的目标。

举例来说,假设有一个学生管理系统,其中有学生信息模块、课程模块和成绩模块。

  • 如果学生信息模块直接修改成绩模块的数据,这就是强耦合,不利于系统的维护和扩展。
  • 而如果每个模块都专注于自己明确的功能,如学生信息模块只负责管理学生的基本信息,成绩模块只负责成绩的相关操作,这就是高内聚,使得每个模块的功能清晰、单一,易于理解和维护。

低耦合和高内聚的设计可以提高软件的可维护性、可扩展性和可重用性,降低软件开发和维护的成本。

控制反转 是一种设计原则,它指的是控制权从应用程序代码转移到了外部的框架或容器。在传统的编程中,对象的创建和依赖关系的管理通常由应用程序自身负责。而在控制反转的理念下,这些控制权被交给了外部的机制。

依赖注入 是实现控制反转的一种方式。它是指在一个对象创建或使用的时候,将其依赖的对象通过外部的方式(例如构造函数、属性设置方法等)传递给它,而不是由对象自身去创建或获取这些依赖。

IOC&DI入门

@Component 注解用于将一个普通的 Java 类标记为一个 Spring 管理的组件。这意味着 Spring 容器会负责创建该类的实例(bean),并对其进行管理(包括依赖注入等)。

@Autowired 是 Spring 框架中的一个注解,用于实现依赖自动注入。

当一个类中的成员变量、方法参数或构造函数参数被标注为 @Autowired 时,Spring 框架会自动查找匹配类型的 bean,并将其注入到该成员变量、方法参数或构造函数参数中。

 七.IOC详解

respon除了 @Component 注解,通常还有一些具有更具体语义的衍生注解,如:

  1. @Service:用于标注服务层的组件。
  2. @Repository:用于标注数据访问层(DAO 层)的组件。
  3. @Controller:用于标注控制层(MVC 架构中的控制器)的组件。

这些注解的功能与 @Component 类似,但更明确地表明了组件的类型和用途,有助于提高代码的可读性和可维护性。

 @Controller也可以不用添加,前面讲过,@RestController注解里包含了这个注解

可以在注解后面加(“name”)用来给此bean对象署名

默认类名首字母小写

组件扫描

八.DI详解

 如果多个继承相同接口的bean对象都依赖注入的话,会发生矛盾

用以下方式,处理矛盾

@Primary注解

@Primary 是 Spring 框架中的注解。当在多个相同类型的 bean 被定义时,使用 @Primary 注解可以指定其中一个作为首要的(primary)bean。当 Spring 进行依赖注入并且需要选择一个该类型的 bean 时,如果存在被标注为 @Primary 的 bean,就会优先选择它。

@Qualifier注解 

“Qualifier”常见中文释义为“限定词;合格者;修饰语”。

@Qualifier 是 Spring 框架中的注解。用于在存在多个相同类型的 bean 时,更精确地指定要注入的 bean。

当多个 bean 属于同一类型,而仅使用 @Autowired 无法明确要注入哪一个时,可以结合 @Qualifier 注解,通过指定 bean 的名称来明确注入的对象。

@Resource注解 

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

默认按照名称进行依赖注入,如果找不到与名称匹配的 bean,则按照类型进行匹配注入。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【剑指 offer】删除链表中重复的结点

目 录 描述: 在一个排序的链表中&#xff0c;存在重复的结点&#xff0c;请删除该链表中重复的结点&#xff0c;重复的结点不保留&#xff0c;返回链表头指针。 例如&#xff0c;链表 1->2->3->3->4->4->5 处理后为 1->2->5 思路&#xff1a; 通过快慢…

2024年阳光电源社招校招入职测评:前程无忧智鼎题库全解析

在职场竞争日益激烈的今天&#xff0c;企业对人才的选拔标准越来越高。阳光电源&#xff0c;作为行业的领军企业&#xff0c;采用了前程无忧智鼎题库进行社招校招入职测评&#xff0c;旨在通过科学的方法选拔出与企业文化和价值观高度契合的人才。 测评概览 测评名称&#xff1…

大模型RAG企业级项目实战:Chatdoc智能助手文档(从零开始,适合新手)

大模型RAG企业级项目实战&#xff1a;Chatdoc智能助手文档&#xff08;从零开始&#xff0c;适合新手&#xff09; 大模型RAG企业级项目实战完整链接 LLM模型缺陷&#xff1a; ​ 知识是有局限性的(缺少垂直领域/非公开知识/数据安全) ​ 知识实时性(训练周期长、成本高) …

5计算机网络全面解析

网络功能和分类 计算机网络是计算机技术与通信技术相结合的产物&#xff0c;它实现了远程通信、远程信息处理和资源共享。 计算机网络的功能&#xff1a;数据通信、资源共享、管理集中化、实现分布式处理、负载均衡。 网络性能指标&#xff1a;速率、带宽&#xff08;频带宽…

1.微服务发展阶段

单体应用阶段 简介 系统业务量很小的时候我们把所有的代码都放在一个项目中&#xff0c;然后将这个项目部署在一台服务器上&#xff0c;整个项目所有的服务都由这台服务器去提供 优点 1.展现层、控制层、持久层全都在一个应用里面&#xff0c;调用方便、快速&#xff0c;单个请…

Unity URP 曲面细分学习笔记

Unity URP 曲面细分学习笔记 1.曲面细分与镶嵌1.1 外壳着色器 Hull Shader1.2 镶嵌器阶段 Tessellator1.3 域着色器阶段 Domain Shader 2.具体实现2.1 不同的细分策略2.1.1 平面镶嵌 Flat Tessellation2.1.2 PN Tessellation&#xff08;Per-Node Tessellation&#xff09;2.1.…

NPM使用教程:从入门到精通

NPM使用教程&#xff1a;从入门到精通&#xff0c;掌握Node.js包管理神器 引言 随着Node.js的流行&#xff0c;JavaScript已经成为服务器端开发的主力军。NPM&#xff08;Node Package Manager&#xff09;作为Node.js的官方包管理工具&#xff0c;为开发者提供了一个庞大的代…

用R的界面来安装扩展包

下面内容摘录自《R 语言与数据科学的终极指南》专栏文章的部分内容&#xff0c;每篇文章都在 5000 字以上&#xff0c;质量平均分高达 94 分&#xff0c;看全文请点击下面链接&#xff1a; 2章4节&#xff1a;认识和安装R的扩展包&#xff0c;什么是模糊搜索安装&#xff0c;工…

Linux高级编程 8.12 标准IO

目录 一、标准io 二、文件 三、man手册 四、操作文件 1.fopen 2.fputc 3.fgetc 4.fgets 5.fputs 6.fread 7.fwrite 五、命令解读 1.显示目录下的文件信息命令 2.vimdiff小工具 3.stat命令 一、标准io IO&#xff1a; input output I&#xff1a; 键盘是标准输…

吃透张宇18讲+1000题,最后能考到110+吗?

张宇18讲太难了怎么办&#xff1f; 当然是换一本 不难的&#xff0c;张宇18讲的难度确实超过大多数同学能够接受的程度&#xff0c;张宇老师也说&#xff0c;默认大家基础已经很好&#xff0c;所以&#xff0c;如果你想很好的利用张宇18讲&#xff0c;必须要有一个一个很好的基…

ViT——探索自监督视觉Transformers在深度伪造检测中的应用

介绍 论文地址&#xff1a;https://arxiv.org/abs/2405.00355 在这篇评论文章中&#xff0c;我们研究了自监督、预训练Transformers与监督、预训练Transformers和传统神经网络&#xff08;ConvNets&#xff09;相比&#xff0c;在检测深度伪造方面的效果如何。 我们尤其关注…

开源的 P2P 跨平台传文件应用「GitHub 热点速览」

就在上周&#xff0c;发完那篇文章之后不久&#xff0c;我就有幸获得了 GitHub Models 服务公测的访问权限&#xff0c;所以就体验了一下 Playground 聊天功能。 起初&#xff0c;我以为这是“微软菩萨”降临&#xff0c;但玩了一圈下来&#xff0c;发现实际效果并没有那么惊艳…

❤️【接口测试面试】精选50接口测试面试题目,或许能帮到金九银十面试浪潮中的你❤️

基本理论知识 1、什么是(软件)接口测试? 接口测试&#xff1a;是测试系统组件间接口的一种测试方法 接口测试的重点&#xff1a;检查数据的交换&#xff0c;数据传递的正确性&#xff0c;以及接口间的逻辑依赖关系 接口测试的意义&#xff1a;在较早期开展&#xff0c;在软…

iPhone官方商店软件下载---免费看各种剧第⑤弹【iOS版包括iPad】

①点击iPhone自带软件App Store ②点击搜索&#xff0c;输入“周末趣味活动” &#xff0c;点击下载到手机 ③进入软件页面后&#xff0c;我们需要激活页面&#xff0c;点击“feedback” &#xff0c;输入“周六日”&#xff0c;点击“summit” ④等软件闪退后&#xff0c;再点…

智能化清理C盘的方法 小白也可以轻松清理C盘了 不再担心误删文件

智能化清理C盘的方法 小白用户也可以轻松清理C盘了 不再担心误删文件。对于电脑小白来说&#xff0c;C盘清理是一个大大的问题&#xff0c;因为大家都不知道C盘里有哪些文件可以删除&#xff0c;哪些不能删除&#xff0c;所以就直接的导致大家不可能去清理c盘垃圾。 就算是C盘…

一次sql请求,返回分页数据和总条数

日常搬砖&#xff0c;总少不了需要获取分页数据和总行数。 一直以来的实践是编码两次sql请求&#xff0c;分别拉分页数据和totalCount。 最近我在思考&#xff1a; 常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新&#xff0c;显而易见的优势&#xff1a; ① 能…

opencv 控制鼠标键盘实现功能setMouseCallback

鼠标事件类型 OpenCV 支持多种鼠标事件类型&#xff0c;常见的包括&#xff1a; cv2.EVENT_LBUTTONDOWN&#xff1a;左键按下 cv2.EVENT_RBUTTONDOWN&#xff1a;右键按下 cv2.EVENT_MBUTTONDOWN&#xff1a;中键按下 cv2.EVENT_LBUTTONUP&#xff1a;左键释放 cv2.EVENT_RBUTT…

Vue3从零开始——带你轻松掌握组件的基本操作

文章目录 1. Vue 组件的基础概念1.1 什么是组件&#xff1f;1.2 组件的作用1.3 组件的分类&#xff08;全局组件 vs 局部组件&#xff09; 2. 创建和注册组件2.1 单文件组件&#xff08;SFC&#xff09;2.2 全局组件注册2.3 局部组件注册 3. 组件命名格式4. ref获取DOM元素4.1 …

CSC7225、CSC7224 双绕组24瓦芯片

CSC7225、CSC7224为高性能电流模式 PWM 开关电源控制器&#xff0c;满足绿色环保标准&#xff1b;CSC7225、CSC7224广泛适用于经济型开关电源&#xff0c;如 DVD、机顶盒、传真机、打印机、LCD 显示器等。CSC7225、CSC7224采用 DIP-8 封装。应用原理如下图&#xff1a; CSC7225…

目前最流行的前端构建工具,你知道几个?

现在的市面上有很多不同的前端构建工具&#xff0c;我们很难对它们一一进行关注。在本文中&#xff0c;我们将重点介绍最受欢迎的几种&#xff0c;并探讨开发人员喜欢或不喜欢它们的原因。 Webpack Webpack 是一个模块打包器&#xff0c;主要用于处理 Web 应用程序的资源的优化…