SpringBoot Web开发之请求响应、分层解耦问题记录
作者:DAWN_T17
一.请求响应概述
1.Servlet
在 Spring Boot 的 Web 请求响应处理中,Servlet
起着关键的作用。
Servlet
是 Java Web 开发中的基本组件,主要负责处理客户端的请求并生成响应。
具体来说,它具有以下重要作用:
- 接收请求:Servlet 能够接收来自客户端(如浏览器)发送的 HTTP 请求。
- 处理请求:在接收到请求后,执行相应的业务逻辑处理。这可能包括与数据库交互、进行数据计算、验证用户输入等操作。
- 控制流程:根据请求的类型和参数,决定后续的处理流程和响应方式。
- 生成响应:处理完请求后,生成要返回给客户端的响应数据。
- 与其他组件协作:可以与其他的 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 请求。
它包含了大量与请求相关的信息和方法:
请求方法:例如 GET
、POST
、PUT
、DELETE
等,通过 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 请求的响应。
它包含了一系列方法,用于设置响应的状态码、响应头信息、响应体内容等。
一些常见的方法包括:
setStatus(int status)
:设置响应的状态码,例如200
表示成功,404
表示未找到资源,500
表示服务器内部错误等。setHeader(String name, String value)
:设置响应头信息,如设置Content-Type
来指定响应体的数据类型。getWriter()
:获取一个PrintWriter
对象,用于向响应体中写入字符数据。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 对象)相对应的概念包括
DTO(Data Transfer Object,数据传输对象):主要用于在不同层或不同系统之间传输数据,通常只包含必要的数据字段,并且这些字段通常是只读的。VO(Value Object,值对象):用于表示不可变的值,通常只包含属性和访问这些属性的方法,并且没有任何行为逻辑。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 开发中,服务器端可以获取这个路径参数,并根据其值进行相应的处理。
多个路径参数样式
路径参数具有以下重要作用
- 精确资源定位
- 能够准确地指定要访问的特定资源。例如,在一个博客系统中,/post/123 中的 123 可以准确指向特定的文章。
- 简化 URL 结构
- 使 URL 看起来更简洁和有组织,而不是通过大量的查询参数来传递关键信息。
- 提高路由效率
- 服务器端可以基于路径参数快速进行路由决策,提高请求处理的效率。
- 增强用户体验
- 对于用户来说,直观的路径参数更容易理解和记忆。
- 实现动态内容展示
- 根据不同的路径参数,服务器可以动态地生成和返回不同的内容。
- 便于权限控制和访问管理
- 可以基于路径参数来设置不同的权限规则,控制对特定资源的访问。
例如,在一个在线教育平台,/course/101/lesson/5
这样的路径参数能够清晰地标识特定的课程和课程中的特定章节,服务器可以据此提供准确的教学内容,并进行相应的权限验证。
总结
四.响应
1.响应的实现和过程
基本上都使用 @RestController
注解的控s制器方法来直接返回数据
如下图,@RestController
注解包含了 @ResponseBody注解
响应的数据若是较复杂,以JSON格式传递
但这样还是不方便管理和后期维护,所以有统一的响应格式
2.统一响应格式
常见的统一响应格式可以包含以下几个部分:
status
:表示请求的处理状态,通常是一个整数,例如1表示成功,0表示失败。message
:对状态的简要描述信息,解释请求处理的结果。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 操作库。
它具有以下特点和优势:
- 强大的解析功能:能够有效地解析和处理复杂的 XML 文档。
- 灵活的操作:支持对 XML 节点的创建、修改、删除、查询等操作。
- 易于使用:提供了简洁直观的 API,使得开发人员能够轻松上手。
作用:
- XML 文档解析
能够读取和解析 XML 文档,将其转换为易于操作的 Java 对象结构。
- 节点操作
可以方便地访问、添加、修改和删除 XML 文档中的节点(元素、属性、文本等)。
- 数据提取
从 XML 文档中提取所需的数据,例如特定元素的值或属性的值。
- 构建 XML 文档
能够从零开始创建新的 XML 文档,并按照指定的结构添加内容。
- 遍历文档
支持对 XML 文档进行深度优先或广度优先的遍历,以便处理文档中的各个部分。
- 与其他系统集成
在需要与基于 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" 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异步访问后端,后端再发送给前端,随即渲染展示到页面
浏览器先向服务器请求页面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 提供的明确接口进行交互,这就是弱耦合。
耦合的类型包括:
- 内容耦合:一个模块直接访问另一个模块的内部数据或代码。这是最强且最不好的耦合形式。
- 公共耦合:多个模块都访问同一个全局数据环境。
- 控制耦合:一个模块通过传递控制信息(如标志、开关量等)来影响另一个模块的功能。
- 标记耦合:两个模块之间通过参数传递复杂的数据结构。
- 数据耦合:模块之间通过参数传递基本数据类型的数据。这是理想的、低耦合的方式。
内聚 则衡量的是一个模块内部各个元素之间的关联程度。
内聚程度高表示模块内部的元素紧密相关,共同完成一个明确、单一的功能。
例如,一个专门负责处理学生成绩计算的模块,如果它只包含与成绩计算相关的代码和数据,就是高内聚。
低内聚的情况比如一个模块既处理成绩计算,又处理学生的课程安排,功能过于混杂。
在软件设计中,我们通常希望达到低耦合高内聚的目标。
举例来说,假设有一个学生管理系统,其中有学生信息模块、课程模块和成绩模块。
- 如果学生信息模块直接修改成绩模块的数据,这就是强耦合,不利于系统的维护和扩展。
- 而如果每个模块都专注于自己明确的功能,如学生信息模块只负责管理学生的基本信息,成绩模块只负责成绩的相关操作,这就是高内聚,使得每个模块的功能清晰、单一,易于理解和维护。
低耦合和高内聚的设计可以提高软件的可维护性、可扩展性和可重用性,降低软件开发和维护的成本。
控制反转 是一种设计原则,它指的是控制权从应用程序代码转移到了外部的框架或容器。在传统的编程中,对象的创建和依赖关系的管理通常由应用程序自身负责。而在控制反转的理念下,这些控制权被交给了外部的机制。
依赖注入 是实现控制反转的一种方式。它是指在一个对象创建或使用的时候,将其依赖的对象通过外部的方式(例如构造函数、属性设置方法等)传递给它,而不是由对象自身去创建或获取这些依赖。
IOC&DI入门
@Component
注解用于将一个普通的 Java 类标记为一个 Spring 管理的组件。这意味着 Spring 容器会负责创建该类的实例(bean),并对其进行管理(包括依赖注入等)。
@Autowired
是 Spring 框架中的一个注解,用于实现依赖自动注入。
当一个类中的成员变量、方法参数或构造函数参数被标注为 @Autowired
时,Spring 框架会自动查找匹配类型的 bean,并将其注入到该成员变量、方法参数或构造函数参数中。
七.IOC详解
respon除了 @Component
注解,通常还有一些具有更具体语义的衍生注解,如:
@Service
:用于标注服务层的组件。@Repository
:用于标注数据访问层(DAO 层)的组件。@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,则按照类型进行匹配注入。
到此这篇关于SpringBoot Web开发(请求,响应,分层解耦)的文章就介绍到这了,更多相关SpringBoot Web请求响应内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!