基于SpringBoot的三层架构开发统一响应结果

news2024/12/26 0:17:54

说明:三层架构开发时目前开发的主流,我这里通过一个案例,来分析非三层架构开发的不利之处,以及三层架构开发的好处。

案例说明:打开员工信息页,页面要显示所有员工的信息;前端页面已提供,后端通过读取本地emp.xml文件,显示员工信息或者提供本地资源(图片资源存放在项目中的resources文件夹中)的路径返回给前端,以此来模拟实际的开发流程(实际开发信息是放在数据库中的)。

分析:后端需要做的,就是读取员工信息,封装成一个对象,返回给前端。
在这里插入图片描述

前端页面如下,需要注意axios.get()中的地址,表示后台查询员工所有信息的方法所需要的映射地址要与这个一致。

<!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('/listEmp2').then(res=>{
                if(res.data.code){
                    this.tableData = res.data.data;
                }
            });
        },
        methods: {
        }
    });
</script>
</html>

资源放在resources文件夹下
在这里插入图片描述

存放员工信息的emp.xml文件放在桌面文件夹里
在这里插入图片描述

统一响应结果

开始之前,先介绍一下统一响应结果。因为后端返回给前端的数据,数据类型是不可确定的,不可能每次都前后端开发人员沟通协商,这样就增加了沟通的成本。所以,可以做一个约定,后端把反馈的结果封装成一个对象,对象的属性里有:请求处理状态、请求的信息、数据三个属性。以后所有的请求,反馈结果的都是这样一个对象。前端开发人员就可以通过对象的值判断请求是否处理成功、提示什么信息、以及附带的数据,以供使用。

package com.essay.utils;

/**
 * 统一响应结果
 */
public class Result {
    /**
     * 请求状态:1表示成功、0表示失败
     */
    private Integer code;
    /**
     * 请求信息
     */
    private String msg;
    /**
     * 数据
     */
    private Object data;
    
	/* set()、get()、toString()方法 */

    /**
     * 请求失败的处理方案
     * @param msg 失败信息
     * @return
     */
    public static Result error(String msg) {
        Result result = new Result();
        result.setCode(0);
        result.setMsg(msg);
        return result;
    }

    /**
     * 请求成功的处理方案
     * @param msg 成功信息
     * @param obj 数据
     * @return
     */
    public static Result success(String msg, Object obj) {
        Result result = new Result();
        result.setCode(1);
        result.setMsg(msg);
        result.setData(obj);
        return result;
    }
}

将这个类,放在项目的utils包中,同时还需要一个解析xml文件的工具类(这里面的代码不是本文的重点,只需知道是用来解析xml文件的就行)
在这里插入图片描述

pom.xml文件中需要添加依赖(SpringBoot依赖、解析xml文件工具类所需的依赖)

    <!--springboot项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!--页面项目所需依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--解析xml文件所需依赖-->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
    </dependencies>

非三层架构开发

接下来,用非三层架构的方式,来实现这个查询所有员工信息的功能;

第一步:创建Emp类

package com.essay.domain;

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;
    }
	
	/* set()、get()、toString()方法 */
}

第二步:创建EmpComtroller类和启动类

EmpComtroller类,即实现查询员工所有信息功能的类

package com.essay.web;

import com.essay.domain.Emp;
import com.essay.utils.Result;
import com.essay.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class EmpContorller {

    @RequestMapping("/listEmp")
    public Result listEmp() {
        // 读取emp.xml文件中的信息
        String path = "C:\\Users\\1\\Desktop\\Emp\\emp.xml";
        List<Emp> list = XmlParserUtils.parse(path, Emp.class);

        // 将返回的员工信息集合封装成Result对象,返回给前端
        return Result.success("成功", list);
    }
}

启动类

package com.essay;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Start {
    public static void main(String[] args) {
        SpringApplication.run(Start.class, args);
    }
}

第三步:启动

功能做完,启动项目
在这里插入图片描述

可以看到,功能实现了,只是员工的信息中,性别、职务分别是用数字表示的(以后数据库中也是类似这样表示的),因此还需对返回的数据进一步处理。比如性别中1表示男性,2表示女性;职务中,1表示总经理、2表示部门经理之类的;

    @RequestMapping("/listEmp")
    public Result listEmp() {
        // 读取emp.xml文件中的信息
        String path = "C:\\Users\\1\\Desktop\\Emp\\emp.xml";
        List<Emp> list = XmlParserUtils.parse(path, Emp.class);

        // 对集合中的数据进行处理
        list.stream().forEach(emp -> {
            if ("1".equals(emp.getGender())){
                emp.setGender("男性");
            }
            if ("2".equals(emp.getGender())){
                emp.setGender("女性");
            }
            if ("1".equals(emp.getJob())){
                emp.setJob("总经理");
            }
            if ("2".equals(emp.getJob())){
                emp.setJob("部门经理");
            }
            if ("3".equals(emp.getJob())){
                emp.setJob("组员");
            }
        });

        // 将返回的员工信息集合封装成Result对象,返回给前端
        return Result.success("成功", list);
    }

处理后重新启动,可以看到,达到预想的结果了。
在这里插入图片描述

小结

目前的开发结构,可以看出功能内的代码逻辑性差,结构松散,违背单一职责原则,业务稍有改动,类中的代码都需要同步修改(比如xml文件路径更改了,职务中的数字表示的含义发生变化了,类中的代码都有同步修改),这是非三层架构开发的缺点。

分析

通过分析以上代码,一个查询功能的操作分为三步:

第一步,读取xml文件(访问数据库),获取员工信息;【数据访问层】

第二步,对数据进行处理,将员工信息中的性别、职务转换为文字表述;【业务逻辑层】

第三步,将查询结果封装成一个Result对象,返回给前端;【控制层/页面层】

如果以后所有的请求都按照这三步来划分,将请求一层一层去处理。就便于管理代码,提高代码的维护性,这就是三层架构开发。

三层架构开发

在前面的基础上,把查询员工信息功能的代码分成三部分:

控制层/页面层(Controller):接收请求,调用业务逻辑对象处理,返回处理结果;

业务逻辑层(Service):调用数据访问对象查询数据,处理数据;

数据访问层(Dao):解析emp.xml文件,读取员工信息;

修改代码如下:

控制层/页面层(Controller):接收请求,响应数据

import com.essay.domain.Emp;
import com.essay.service.EmpService;
import com.essay.service.impl.EmpServiceImpl;
import com.essay.utils.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 控制层/页面层(Controller)
 */
@RestController
public class EmpContorller {

    /**
     * 创建业务逻辑对象
     */
    private EmpService empService = new EmpServiceImpl();

    @RequestMapping("/listEmp")
    public Result listEmp() {

        // 调用service去处理逻辑
        List<Emp> list = empService.listEmp();

        // 返回响应结果
        return Result.success("成功", list);
    }
}

业务逻辑层(Service):逻辑处理

import com.essay.dao.EmpDao;
import com.essay.dao.impl.EmplDaoImpl;
import com.essay.domain.Emp;
import com.essay.service.EmpService;

import java.util.List;

/**
 * 业务逻辑层(Service)
 */
public class EmpServiceImpl implements EmpService {

    /**
     * 创建数据访问对象
     */
    private EmpDao empDao = new EmplDaoImpl();

    /**
     * 处理查询所有员工的业务
     */
    @Override
    public List<Emp> listEmp() {
        // 数据来源于Dao
        List<Emp> list = empDao.listEmp();

        // 对集合中的数据进行处理
        list.stream().forEach(emp -> {
            if ("1".equals(emp.getGender())) {
                emp.setGender("男性");
            }
            if ("2".equals(emp.getGender())) {
                emp.setGender("女性");
            }
            if ("1".equals(emp.getJob())) {
                emp.setJob("总经理");
            }
            if ("2".equals(emp.getJob())) {
                emp.setJob("财务");
            }
            if ("3".equals(emp.getJob())) {
                emp.setJob("组员");
            }
        });

        return list;
    }
}

数据访问层(Dao):数据访问

import com.essay.dao.EmpDao;
import com.essay.domain.Emp;
import com.essay.utils.XmlParserUtils;

import java.util.List;

/**
 * 数据访问层(Dao)
 */
public class EmplDaoImpl implements EmpDao {

    @Override
    public List<Emp> listEmp() {
        // 读取emp.xml文件中的信息
        String path = "C:\\Users\\1\\Desktop\\Emp\\emp.xml";
        List<Emp> list = XmlParserUtils.parse(path, Emp.class);

        return list;
    }
}

实际上,会给业务逻辑层(Service)、数据访问层(Dao)各自创建一个接口,让其实现类去执行操作;

业务逻辑对象(Service)接口

import com.essay.domain.Emp;

import java.util.List;

/**
 * 业务逻辑对象(Service)接口
 */
public interface EmpService {
    /**
     * 查询所有员工信息
     * @return
     */
    List<Emp> listEmp();
}

数据访问层(Dao)接口

import com.essay.domain.Emp;

import java.util.List;

/**
 * 数据访问对象接口
 */
public interface EmpDao {
    /**
     * 查询所有员工信息
     * @return
     */
    List<Emp> listEmp();
}

增加注解

虽然使用了三层架构,但层与层之间的耦合性还是很高的,如果项目中开发了多套ServiceImpl(业务处理方案),想要在多套处理方案中切换的话,就只能修改代码,手动选择其中的一套。
在这里插入图片描述

这时就可以使用Spring提供的注解,来帮我们自动创建、使用对象,

@Repository注解:表示当前类为数据访问层对象
在这里插入图片描述

@Service注解:表示当前类为业务逻辑层对象
在这里插入图片描述

@Controller注解:表示当前类为控制/页面层对象(@RestController包括了@Controller,所以使用@RestController也可以)
在这里插入图片描述

然后需要创建对象时,使用@AutoWired注解,程序会自动根据类型寻找对应的类对象
在这里插入图片描述
在这里插入图片描述

启动

大功告成,启动在这里插入图片描述
到此,基于SpringBoot的三层架构开发的查询所有员工信息的功能就完成了。

控制反转&依赖注入

上面所用到的注解,是Spring的一个特性,即控制反转&依赖注入。

在程序启动时,启动类会扫描本包和子包,识别到对应注解(@Service、@Repository等)的类,将类创建到IOC容器中,这个过程称为控制反转;之后在需要使用到对象时(@Autowired),会从IOC容器中取出来,这个过程称为依赖注入。

因为启动类启动时只会扫描子包,所以启动类不能放在某个包里面,或者说启动类要在所有子包之上,否则本包之外和启动类之上的包中标记的注解不会生效。
在这里插入图片描述

启动程序后,可以在Actuator的Beans中找到我们三层架构的类对象;
在这里插入图片描述

@Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean,程序会报错
在这里插入图片描述

可以通过以下方式解决:

(1)使用@Primary注解,设置“主要的bean”,发生重名时,优先使用该bean;
在这里插入图片描述

(2)使用@Qualifier注解,指名取指定的bean;如果类没有设置名称的话,@Qualifier里面填需要使用的bean类名(bean名为类名的首字母小写)
在这里插入图片描述
在这里插入图片描述

(3)使用JDK的@Resource注解,指名取指定的类对象。不属于Spring的注解,且使用了此注解,上面就不需要加@Autowired注解了。这种方式与前两种方式格格不入,故不推荐使用;
在这里插入图片描述

总结

三层架构开发,把一个前端请求分为三个部分,层层递进,增强了代码的逻辑,提高了代码的复用性,便于后续的维护。

另外,使用统一响应结果,统一了返回给前端页面的结果,减少了前后端开发人员的交流成本,也更规范了程序开发。

最后,在三层架构的基础上,使用Spring注解,将管理对象的工作交给了IOC容器,降低了层与层之间依赖、关联程度,实现了系统的高内聚低耦合。

参考:【黑马程序员2023新版JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等)】 https://www.bilibili.com/video/BV1m84y1w7Tb/?p=79&share_source=copy_web&vd_source=8d1a3172aa5ba3ea7bdfa82e535732a8 (P73-79)

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

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

相关文章

cs109-energy+哈佛大学能源探索项目 Part-3(探索性分析)

博主前期相关的博客见下&#xff1a; cs109-energy哈佛大学能源探索项目 Part-1&#xff08;项目背景&#xff09; cs109-energy哈佛大学能源探索项目 Part-2.1&#xff08;Data Wrangling&#xff09; cs109-energy哈佛大学能源探索项目 Part-2.2&#xff08;Data Wrangling&a…

Java设计模式(三)

系列文章目录 迪米特法则 合成复用原则 设计原则核心思想 文章目录 系列文章目录前言一、迪米特法则1.迪米特法则基本介绍2.迪米特法则注意事项和细节 二、合成复用原则1.合成复用原则基本介绍 三、设计原则核心思想总结 前言 大家好呀&#xff0c;欢迎来到柚子的博客~让我们…

强化学习笔记-08 Planning and Learning

前几篇我们介绍了一个重点概念model-base和model-free&#xff0c;其中model-base是建立在存在某个环境模型&#xff0c;从模型中可以获得状态、动作、奖励的转移信息&#xff0c;比如动态规划方法&#xff0c;当我们确定了状态动作的转换概率&#xff0c;此时我们可以通过递归…

寄存器某个位赋值、宏定义、头文件的条件编译及全局声明extern的使用

一、给寄存器的某个位进行赋值 C语言基础知识&#xff08;位操作&#xff09; 运算符含义运算符含义&按位与~按位取反|按位或<<左移^按位异或>>右移 我们定义一个32位的寄存器变量&#xff1a;unit32_t temp0; 从左到右依次是第1位&#xff0c;第2位......第…

网络层:地址解析协议ARP

网络层&#xff1a;地址解析协议ARP 笔记来源&#xff1a; 湖科大教书匠&#xff1a;ARP协议 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 B知道C的IP地址但如果不知道其对应的MAC地址&#xff0c;就无法将数据包发送给C&#xff0c;如何解决这个…

新款Model 3长什么样?特斯拉已经开始降价?

作者 | Amy 编辑 | 德新 马斯克时隔三年再次访华&#xff0c;期间会见了多部委领导及重量级人物&#xff0c;在北京和上海短暂停留了44小时后便离开了中国。 此前曾有多家媒体猜测马斯克可能会在中国发布/展示改款Model 3。但据彭博社报道&#xff0c;马斯克只是在上海工厂看了…

面向对象编程 实验三 sduwh 子窗口与控件的基本用法、资源的使用 参考实验报告2

源自网络收集&#xff0c;仅供参考 实验三收集到两份完整报告&#xff0c;这是其二&#xff0c;另一份见本专栏上一篇文章。 1 实验要求 整体目的&#xff1a;理解、窗口之间的消息传送、调用栈&#xff1b;掌握光标、图标、菜单的制作和使用方 式 &#xff1b; 掌 握 菜 单…

【Vue】三:Vue核心处理---vue的其它指令和自定义指令

文章目录 1.vue的其它指令1.1v-text 和 v-html1.2 v-cloak1.3 v-once1.4 v-pre 2. 自定义指令2.1 函数式2.2 对象式2.3.定义全局的指令 1.vue的其它指令 1.1v-text 和 v-html v-text&#xff1a;当做文件解析。 v-html&#xff1a;当做 HTML 代码解析。 1.2 v-cloak v-cloa…

在自定义数据集上训练 YOLOv8——完整教程

Ultralytics 最近发布了 YOLOv8 系列对象检测模型。这些模型在 COCO 数据集上的速度和准确性都优于之前版本的 YOLO 模型。但是自定义数据集的性能如何呢?为了回答这个问题,我们将在自定义数据集上训练 YOLOv8 模型。具体来说,我们将在大规模坑洞检测数据集上对其进行训练。…

电子模块|压力传感器模块HX711---C51STM32驱动

电子模块|压力传感器模块HX711---硬件介绍与C51&&STM32驱动 实物照片模块简介模块特点 软件驱动C51软件代码STM32软件代码 实物照片 模块简介 HX711是一款专为高精度称重传感器而设计的24位A/D转换器芯片。与同类型其它芯片相比&#xff0c;该芯片集成了包括稳压电源、…

Rust 标准库字符串类型String及其46种常用方法

Rust字符串 Rust主要有两种类型的字符串&#xff1a;&str和String &str 由&[u8]表示&#xff0c;UTF-8编码的字符串的引用&#xff0c;字符串字面值&#xff0c;也称作字符串切片。&#xff06;str用于查看字符串中的数据。它的大小是固定的&#xff0c;即它不能…

网络层:IP地址

网络层&#xff1a;IP地址 笔记来源&#xff1a; 湖科大教书匠&#xff1a;IP地址 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 IP地址是因特网(Internet)上的主机和路由器所使用的地址&#xff0c;用于标识两部分信息&#xff1a; 网络编号&am…

Allegro16.6详细教程(一)

第一章User Interface 本章的主要內容介紹allegro操作介面,透過本章學習可以對Allegro的工作介面有了大致的瞭解,同時也能體驗出Allegro PCB Layout時的強大功能。 A.程式的啟動 除了以上Allegro主程式,我們還會常用到以下幾個常用工具: • Padstack Designer (Padstack…

《代码日记》

代码日记 Project :Space1、new_method.py new2.pynew3.pynew4.pynew5.py new5.1.pynew6.py Project :Space 都是在D001289数据集上的测试 1、new_method.py 先划分训练集和测试集 通过稀疏自编码器进行降维至20维度 自编器参数&#xff1a; # 训练自编码器模型autoencoder …

JavaEE进阶 Spring AOP(6/1)

目录 1.什么是AOP 2.Spring AOP可以干什么 3.AOP的组成成分 4.SpringAOP实现步骤 5.SpringAOP的原理 1.什么是AOP AOP是面向切面编程&#xff0c;是一种思想 Spring AOP是一种具体的技术 2.Spring AOP可以干什么 1.用户登录状态的判断 2.统一的日志记录&#xff08;比如记录…

基于深度学习的高精度牙齿健康检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度牙齿健康检测识别系统可用于日常生活中检测牙齿健康状况&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的牙齿目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训练数…

c++代码实现一个线程池(清晰明了,一看就懂)

线程池的应用 在我认知中&#xff0c;任何网络服务器都是一个死循环。这个死循环长下面这个样子。 基本上服务器框架都是基于这个架构而不断开发拓展的。 这个死循环总共分为四个步骤&#xff0c;可以涵盖所有客户端的需求&#xff0c;然而目前绝大多数企业不会用这样的架构。…

带有 Utopia Messenger 的免费 ChatGPT 助手

全世界都喜欢 ChatGPT&#xff01;而且这个工具从未如此易于访问。没有限制、没有VPN、没有代理和佣金。现在您可以使用 Utopia Messenger 的传奇聊天的全部功能。 ChatGPT是每个线上用户的必备工具。它为我们提供任何所需的数据&#xff0c;生成内容&#xff0c;并解决多项任…

[数据集][目标检测]目标检测数据集黄瓜数据集VOC格式1309张

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;1308 标注数量(xml文件个数)&#xff1a;1308 标注类别数&#xff1a;1 标注类别名称:["cucumber"] 每…

图论与算法(1)图论概念

1. 图论与算法 在计算机科学中&#xff0c;图论与算法是两个重要且紧密相关的领域。图论研究图的性质和特征&#xff0c;而算法设计和分析解决问题的方法和步骤。图论提供了一种形式化的方法来描述和分析各种关系和连接&#xff0c;而算法则为解决图相关的问题提供了有效的解决…