后端分层解耦

引入

        在上篇所举的例子中,我们将所有的代码均放在HelloControl方法之中,这样会导致代码的复用性、可读性较差,难以维护。因此我们需

三层架构

        在之前的代码中,代码大体可以分为三部分:数据访问、数据逻辑处理、响应数据。每部分承担不同的职责。三层架构的设计目标是实现一个类或一个方法只实现某单一功能,即单一职责原则。这样即可提高系统的可维护性和可扩展性。

        以下是三层架构的基本组成部分:

  • Controller:控制层/表示层,接收前端发送的请求,对请求进行处理并响应数据
  • Service:业务逻辑层,处理具体的业务逻辑
  • Dao:数据访问层(Data Access Object)负责数据访问操作,包括增删查改等

        分层之后如果需要更改业务处理方式,只需更改更改Service层的代码即可,代码的复用性和可维护性大大提高。

        我们先来看dao层,访问数据的方式有很多,比如文件中的数据、数据库中的数据、接口提供的数据等等,为了能够适应不同数据,可以采用面向接口的方式进行编程:创建Controller同级目录Dao,编写接口(stu_data)规定返回数据,再编写java类(stu1)实现该接口

        Service层的逻辑也类似,于此不再赘述。 总体业务逻辑如下:

  1. 前端发起请求→
  2. controller接收并调用service层方法→
  3. service调用dao层方法→
  4. dao层获取数据并返回到service层→
  5. service层处理数据并返回到controller层→
  6. controller层响应前端

        这样就完成了三层架构的拆分,每层的职责都很单一

分层解耦

        解耦的含义即为解除耦合,这里需要引入两个概念:

  • 内聚:软件中各个模块内部的功能联系
    • 高内聚意味着模块内部的元素紧密相关,共同完成一个单一、明确的功能。
    • 低内聚则意味着模块内部元素之间关联度较低,可能执行多个不相关的功能。
  • 耦合:各个层/模块之间的依赖程序
    • 低耦合意味着模块之间依赖关系较少,每个模块相对独立,易于独立地开发测试和维护。
    • 高耦合则意味着模块之间依赖关系较多,一个模块的变更可能会影响到其他模块。

        在软件设计当中,我们通常需要遵守“高内聚,低耦合”的原则,即模块内部各功能联系紧密,但与其他模块依赖较低。

        上文的例子中,比如controller接收并调用service层方法,需要在controller创建service对象的实例,这样一旦service对象内部的类发生变化,会导致多层瘫痪。但如果直接将实例删掉会导致运行时报错:空指针异常。

        为避免这一问题,我们选择将service对象的实例放入到一个容器中,controller层需要使用时从容器中取得该实例并进行使用,这就是控制反转(IOC)和依赖注入(DI)。

IOC&DI

介绍

        控制反转(IOC)意为对象的创建和绑定由外部容器或框架来管理,而不是由应用程序的代码直接控制。使对象实例成为IOC容器中的bean。
        依赖注入(DI)是一种实现控制反转的方式,它将依赖关系注入到组件中,而不是由组件自己创建依赖关系。运行时,IOC容器会提供该类型的bean对象并赋值。

        实现IOC需要在各个层顶部加上注解@Component,实现DI则是在使用对象实例时添加注解 @Autowired即可:

@Component//实现控制反转
public class StuService1 implements StuService{@Autowired//实现依赖注入private StuDao stuDao;
//之前的代码private StuDao stuDao=new stu1();
//相关方法
}

        如果不需要使用该层,只需将该层的注解注释掉即可。

IOC控制反转

        在spring框架中,为了更好的标识bean对象到底属于哪一层,除了@Component注解外,还提供了三个衍生注解

注解说明位置
@Component声明bean的基础注解

不属于三层时

使用此注解

@Controller@Component的衍生注解Controller层
@Service@Component的衍生注解

Service层

@Repository@Component的衍生注解Dao层

        Service层和Dao层的注解按要求添加即可,Controller层因为存在注解@RestController已包含注解@Controller,因此无需再添加。
        按住ctrl键点击注解,可以看到其内部只封装了一个注解@Component,其余三个均为元注解,因此我们称这三个注解为@Component的衍生注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {@AliasFor(annotation = Component.class)String value() default "";
}

        上文提到使用注解即可使对象实例成为IOC容器中的bean,而在IOC容器中每个bean都有其标识,也就是其名字,在声明bean时,我们可以通过注解中的value属性来指定bean的值,如果未指定则为默认名:该类的类名首字母小写。如果需要指定名称,只需在注解后加上(value = "指定值")或者( "指定值")即可(一般直接使用默认即可)。

@Service(value = "mybean")
//或者@Service("mybean")
public class StuService1 implements StuService{@Autowiredprivate StuDao stuDao;
//相关方法
}

        点击控制台右边的Actuator即可查看所有的bean,其中白色背景的为自定义bean,点击后右方就会出现对应的bean的名字,我们可以看到首字母是小写的。

bean组件扫描

        但加了注解bean也有不生效的可能,之前介绍的四大注解还需要被组件扫描注解@ComponentScan扫描,该注解虽未显式的配置,但实际上已包含在启动类声明注解@SpringBootApplication中,扫描范围默认为启动类所在的包及其子包。
        想要其扫描其他包需要在头部添加注解@ComponentScan(),在该注解内部我们可以看到通过两个属性value和basePackages都可以指定,而这两个属性都是数组,所以应遵循数组的写法@ComponentScan({"原包名","新添加包名"}),注意这里等于覆盖了原来的路径,所以不能只写新添加的包名,应两者都写。但此种做法不推荐,还是建议遵循Springboot的规范将所有文件都放在同一软件包中。

//启动类
@ComponentScan({"myweb1","myweb2"})
@SpringBootApplication
public class MywebApplication {public static void main(String[] args) {SpringApplication.run(MywebApplication.class, args);}
}
//@ComponentScan注解内部
public @interface ComponentScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};//略
}

DI依赖注入

        依赖注入是一种实现控制反转的方式,它将依赖关系注入到组件中,而不是由组件自己创建依赖关系。在之前的例子中,我们通过注解@Autowired就完成了这一系列操作:

@Service
public class StuService1 implements StuService{@Autowired//实现依赖注入private StuDao stuDao;
//相关方法
}

        其意为自动装配,默认为按照类型装配,也就是在该例中,其会到容器中查找StuDao类的bean对象是否存在,但如果存在多个同一类型的bean,系统将会报错,可通过以下三个注解来解决该问题:

  • @Primary:与四大注解搭配使用,提高优先级,有该注解的实例优先成为bean
  • @Qualifier:与@Autowired搭配使用用于指定要注入的Bean的ID
  • @Resource:使用@Resource注解替换@Autowired注解用于指定要注入的Bean的名称,但默认情况下它是基于名称匹配的,而@Autowired是基于类型匹配。
@Primary//提高该实例的优先级,优先转为bean
@Service
public class StuService1 implements StuService{//略
}//————————————————————————————————————————————————
@Service
public class StuService1 implements StuService{@Qualifier("stuDao")//指定注入该依赖@Autowiredprivate StuDao stuDao;//略
}//————————————————————————————————————————————————
@Resource(name = "stuDao")
@Service
public class StuService1 implements StuService{@Resource(name = "stuDao")private StuDao stuDao;//略
}

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

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

相关文章

97.【C语言】数据结构之栈

目录 栈 1.基本概念 2.提炼要点 3.概念选择题 4.栈的实现 栈初始化函数 入栈函数 出栈函数和栈顶函数 栈顶函数 栈销毁函数 栈 基本概念参见王爽老师的《汇编语言 第四版》第56和57页 节选一部分 1.基本概念 注意:这里提到的数据结构中的栈有别于操作系统的栈,后者是…

初识算法 · 模拟(1)

目录 前言: 替换所有的问号 题目解析 算法原理 算法编写 提莫攻击 题目解析 算法原理 算法编写 外观数列 题目解析 算法原理 算法编写 前言: ​本文的主题是模拟,通过三道题目讲解,一道是提莫攻击,一道是…

【数值分析】高斯-赛德尔方法、规范化幂法、原点移位法

【数值分析】高斯-赛德尔方法、规范化幂法、原点移位法 题目 要求 代码实现过程不能调用任何库函数自带的“线性 方程组求解、特征值求解库函数” 利用高斯-赛德尔方法求解上述线性方程组 使用Python编程求解矩阵A与列向量b import numpy as np import sympy as spdef crea…

【CUDA】了解GPU架构

目录 一、初步认识 二、Fermi架构 三、Kepler 架构 3.1 动态并行 3.2 Hyper-Q 一、初步认识 SM(Streaming Multiprocessors)是GPU架构中非常重要的部分,GPU硬件的并行性就是由SM决定的。以Fermi架构为例,其包含以下主要组成…

64位程序调用32位dll解决方案

最近在做64位代码移植,发现很多老代码使用到了第三方的32位dll;而且这些第三方32位dll库已经年代久远,原开发商已不再了;所以急切的需要在64位主程序 中使用老的32位dll;查询很多解决方案 发现目前只有使用com 进程外组件的方法可以解决此问题…

【HOT100第五天】搜索二维矩阵 II,相交链表,反转链表,回文链表

240.搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 先动手写写最简单方法,二重循环。 class Solution { public:bool searchMa…

模板元函数应用:输出字符串。

看下面三个字符串,s1,s2,s3 : string s1 "逆天邪神";wstring s2 _t("焚星妖莲");_string s3 "焚绝尘"; 在控制台输出字符串,可能的一个方案是: void print_test(const wstring& s) {std::…

pytest | 框架的简单使用

这里写目录标题 单个文件测试方法执行测试套件的子集测试名称的子字符串根据应用的标记进行选择 其他常见的测试命令 pytest框架的使用示例 pytest将运行当前目录及其子目录中test_*.py或 *_test.py 形式的所有 文件 文件内的函数名称可以test* 或者test_* 开头 单个文件测试…

【C++】类和对象-深度剖析默认成员函数-上

> 🍃 本系列为初阶C的内容,如果感兴趣,欢迎订阅🚩 > 🎊个人主页:[小编的个人主页])小编的个人主页 > 🎀 🎉欢迎大家点赞👍收藏⭐文章 > ✌️ 🤞 &#x1…

Web性能优化:从基础到高级

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 Web性能优化:从基础到高级 Web性能优化:从基础到高级 Web性能优化:从基础到高级 引言 基础优…

当 docker-compose.yaml 文件部署时,Dify 线上版本升级过程

如果线上 Dify 是通过 docker-compose.yaml 文件部署的,那么当 Dify 版本升级时该如何操作呢?官方已经给出了 Docker compose 和 Source Code 两种方式。相对而言,前者更简单些,至少不需要安装依赖包和迁移数据库文件。为了更加具…

如何让手机ip变成动态

在数字化浪潮中,手机已成为我们日常生活中不可或缺的一部分。无论是浏览网页、使用社交媒体还是进行在线购物,手机都扮演着举足轻重的角色。然而,在享受网络带来的便利时,我们也需要关注网络安全和隐私保护。静态IP地址可能让手机…

vue3 如何调用第三方npm包内部的 pinia 状态管理库方法

抛砖引玉: 如果在开发vue3项目是, 引用了npm第三方包 ,而且这个包内使用了Pinia 状态管理库,那我们如何去调用 npm内部的 Pinia 状态管理库呢? 实际遇到的问题: 今天在制作npm包时遇到的问题,之前Vue2版本的时候状态管理库用的Vuex ,当时调用npm包内的状态管理库很简单,直接引…

Linux笔记---调试工具GDB(gdb)

1. gdb的概念 GDB,全称GNU Debugger,是一个功能强大的开源调试工具,广泛用于Unix和类Unix系统,以及Microsoft Windows和macOS平台。GDB允许开发者在程序执行过程中查看内部运行情况,帮助定位和修复程序中的错误。 gd…

编译器gcc/g++

gcc 只用来编译C g 编译C/C 1.预处理(进行宏替换/去注释/条件编译/头文件展开等) 先创建 code.c 文件 -E --> 从现在开始,进行程序的翻译,一旦预处理做完,就停下来 -o --> 表明 -o 后面的文件名称 code…

一.安装版本为19c的Oracle数据库管理系统(Oracle系列)

1.数据库版本信息: 版本信息: 或者直接由命令查出来: 2.操作系统的版本信息 3.安装包下载与上传 可以去oracle官网下载也可以从其他人的百度网盘链接中下载: 使用xftp工具或者其他的工具(mobaxterm)上传到l…

DimensionX 部署笔记

目录 生成视频用CogVideoX-5b-I2V 推理代码: DimensionX 生成视频用CogVideoX-5b-I2V 推理代码: 可以生成,从左向右旋转的,也可以生成从上往下旋转的: import torch from diffusers import CogVideoXImageToVideo…

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话,就会报Promise.withResolvers 不是一个方法的错误,原因是Promise.withResolvers是ES15新特性,想了解可参考链接,这里的解决方案是将插件里的涉及到Promise.withResolvers的地…

Node.js | Yarn下载安装与环境配置

一、安装Node.js Yarn 是 Node.js 下的包管理工具,因此想要使用 Yarn 就必须先下载 Node.js。 推荐参考:Node.js | npm下载安装及环境配置教程 二、Yarn安装 打开cmd,输入以下命令: npm install -g yarn检查是否安装成功&…

开源TTS语音克隆神器GPT-SoVITS_V2版本地整合包部署与远程使用生成音频

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 本文主要介绍如何在Windows系统电脑使用整合包一键部署开源TTS语音克隆神器GPT-SoVITS,并结合cpolar内网穿透工…