佛山 两学一做 网站,网站建设公司 首推万维科技,网站开发款计入什么科目,夜无忧论坛官网#x1f4d8;博客主页#xff1a;程序员葵安
#x1faf6;感谢大家点赞#x1f44d;#x1f3fb;收藏⭐评论✍#x1f3fb; 文章目录
一、请求
1.1 postman
1.2 简单参数
1.3 实体参数
1.4 数组集合参数
1.5 日期参数
1.6 JSON参数
1.7 路径参数
二、响应
2…
博客主页程序员葵安
感谢大家点赞收藏⭐评论✍ 文章目录
一、请求
1.1 postman
1.2 简单参数
1.3 实体参数
1.4 数组集合参数
1.5 日期参数
1.6 JSON参数
1.7 路径参数
二、响应
2.1 ResponseBody
2.2 统一响应结果
三、分层解耦
3.1 三层架构
3.2 分层解耦
3.3 IOCDI 一、请求
接收页面传递过来的请求数据
1.1 postman Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。 Postman原是Chrome浏览器的插件可以模拟浏览器向后端服务器发起任何形式(如:get、post)的HTTP请求 使用Postman还可以在发起请求时携带一些请求参数、请求头等信息 作用常用于进行接口测试 特征 简单 实用 美观 大方
postman使用
创建工作空间 创建请求 点击Save保存当前请求 1.2 简单参数
简单参数在向服务器发起请求时向服务器传递的是一些普通的请求数据。 SpringBoot方式 在Springboot的环境中对原始的API进行了封装接收参数的形式更加简单。 如果是简单参数参数名与形参变量名相同定义同名的形参即可接收参数。
RestController
public class RequestController {// http://localhost:8080/simpleParam?nameTomage10// 第1个请求参数 nameTom 参数名:name参数值:Tom// 第2个请求参数 age10 参数名:age , 参数值:10//springboot方式RequestMapping(/simpleParam)public String simpleParam(String name , Integer age ){//形参名和请求参数名保持一致System.out.println(name : age);return OK;}
}
postman测试( GET 请求) postman测试( POST请求 ) 结论不论是GET请求还是POST请求对于简单参数来讲只要保证请求参数名和Controller方法中的形参名保持一致就可以获取到请求参数中的数据值。 请求参数名和形参名不同时没报错但无法接收到请求数据。
可使用Spring提供的RequestParam注解完成映射
RestController
public class RequestController {// http://localhost:8080/simpleParam?nameTomage20// 请求参数名name//springboot方式RequestMapping(/simpleParam)public String simpleParam(RequestParam(name) String username , Integer age ){System.out.println(username : age);return OK;}
} 注意RequestParam中的required属性默认为true默认值也是true代表该请求参数必须传递如果不传递将报错可改为false表示参数可选 1.3 实体参数
在使用简单参数做为数据传递方式时前端传递了多少个请求参数后端controller方法中的形参就要书写多少个。如果请求参数比较多通过上述的方式一个参数一个参数的接收会比较繁琐。
此时我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装需要遵守如下规则请求参数名与实体类的属性名相同 简单实体参数
定义POJO实体类
public class User {private String name;private Integer age;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;}Overridepublic String toString() {return User{ name name \ , age age };}
}
Controller方法
RestController
public class RequestController {//实体参数简单实体对象RequestMapping(/simplePojo)public String simplePojo(User user){System.out.println(user);return OK;}
}
复杂实体对象
复杂实体对象指的是在实体类中有一个或多个属性也是实体对象类型的。如下 User类中有一个Address类型的属性Address是一个实体类 复杂实体对象的封装需要遵守如下规则 请求参数名与形参对象属性名相同按照对象层次结构关系即可接收嵌套实体类属性参数。
定义POJO实体类 Address实体类
public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province province;}public String getCity() {return city;}public void setCity(String city) {this.city city;}Overridepublic String toString() {return Address{ province province \ , city city \ };}
}
User实体类
public class User {private String name;private Integer age;private Address address; //地址对象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 Address getAddress() {return address;}public void setAddress(Address address) {this.address address;}Overridepublic String toString() {return User{ name name \ , age age , address address };}
}
Controller方法
RestController
public class RequestController {//实体参数复杂实体对象RequestMapping(/complexPojo)public String complexPojo(User user){System.out.println(user);return OK;}
}
1.4 数组集合参数
数组集合参数的使用场景在HTML的表单中有一个表单项是支持多选的(复选框)可以提交选择的多个值。
后端程序接收多个值的方式有两种 数组 集合
数组
数组参数请求参数名与形参数组名称相同且请求参数为多个定义数组类型形参即可接收参数 Controller方法
RestController
public class RequestController {//数组集合参数RequestMapping(/arrayParam)public String arrayParam(String[] hobby){System.out.println(Arrays.toString(hobby));return OK;}
}
Postman测试
在前端请求时有两种传递形式
方式一 xxxxxxxxxx?hobbygamehobbyjava
方式二xxxxxxxxxxxxx?hobbygame,java
集合
集合参数请求参数名与形参集合对象名相同且请求参数为多个RequestParam 绑定参数关系 默认情况下请求中参数名相同的多个值是封装到数组。如果要封装到集合要使用RequestParam绑定参数关系 Controller方法
RestController
public class RequestController {//数组集合参数RequestMapping(/listParam)public String listParam(RequestParam ListString hobby){System.out.println(hobby);return OK;}
}
Postman测试
方式一 xxxxxxxxxx?hobbygamehobbyjava
方式二xxxxxxxxxxxxx?hobbygame,java
1.5 日期参数
在一些特殊的需求中可能会涉及到日期类型数据的封装。
为日期的格式多种多样如2022-12-12 10:05:45 、2022/12/12 10:05:45那么对于日期类型的参数在进行封装的时候需要通过DateTimeFormat注解以及其pattern属性来设置日期的格式。 DateTimeFormat注解的pattern属性中指定了哪种日期格式前端的日期参数就必须按照指定的格式传递。 后端controller方法中需要使用Date类型或LocalDateTime类型来封装传递的参数。
Controller方法
RestController
public class RequestController {//日期时间参数RequestMapping(/dateParam)public String dateParam(DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss) LocalDateTime updateTime){System.out.println(updateTime);return OK;}
}
1.6 JSON参数
JSON是开发中最常用的前后端数据交互方式
我们学习JSON格式参数主要从以下两个方面着手 Postman在发送请求时如何传递json格式的请求参数 在服务端的controller方法中如何接收json格式的请求参数
Postman发送JSON格式数据 服务端Controller方法接收JSON格式数据 传递json格式的参数在Controller中会使用实体类进行封装。 封装规则JSON数据键名与形参对象属性名相同定义POJO类型形参即可接收参数。需要使用 RequestBody标识。 RequestBody注解将JSON数据映射到形参的实体类对象中JSON中的key和实体类中的属性名保持一致
实体类Address
public class Address {private String province;private String city;//省略GET , SET 方法
}
实体类User
public class User {private String name;private Integer age;private Address address;//省略GET , SET 方法
}
Controller方法
RestController
public class RequestController {//JSON参数RequestMapping(/jsonParam)public String jsonParam(RequestBody User user){System.out.println(user);return OK;}
}
1.7 路径参数
在现在的开发中经常还会直接在请求的URL中传递参数。
路径参数 前端通过请求URL直接传递参数 后端使用{…}来标识该路径参数需要使用PathVariable获取路径参数 Controller方法
RestController
public class RequestController {//路径参数RequestMapping(/path/{id})public String pathParam(PathVariable Integer id){System.out.println(id);return OK;}
}
传递多个路径参数
Controller方法
RestController
public class RequestController {//路径参数RequestMapping(/path/{id}/{name})public String pathParam2(PathVariable Integer id, PathVariable String name){System.out.println(id : name);return OK;}
}
二、响应
前面我们学习过HTTL协议的交互方式请求响应模式有请求就有响应
那么Controller程序呢除了接收请求外还可以进行响应。
2.1 ResponseBody
在我们前面所编写的controller方法中都已经设置了响应数据。 controller方法中的return的结果使用了ResponseBody注解响应给浏览器。
ResponseBody注解 类型方法注解、类注解 位置书写在Controller方法上或类上 作用将方法返回值直接响应给浏览器 如果返回值类型是实体对象/集合将会转换为JSON格式后在响应给浏览器
但是在我们所书写的Controller中只在类上添加了RestController注解、方法添加了RequestMapping注解并没有使用ResponseBody注解怎么给浏览器响应呢
RestController
public class HelloController {RequestMapping(/hello)public String hello(){System.out.println(Hello World ~);return Hello World ~;}
}
原因在类上添加的RestController注解是一个组合注解。 RestController Controller ResponseBody
结论在类上添加RestController就相当于添加了ResponseBody注解。 类上有RestController注解或ResponseBody注解时表示当前类下所有的方法返回值做为响应数据 方法的返回值如果是一个POJO对象或集合时会先转换为JSON格式在响应给浏览器
2.2 统一响应结果
大家有没有发现一个问题我们在前面所编写的这些Controller方法中返回值各种各样没有任何的规范。 在真实的项目开发中我们会定义一个统一的返回结果。方案如下 前端只需要按照统一格式的返回结果进行解析(仅一种解析方案)就可以拿到数据。 统一的返回结果使用类来描述在这个结果中包含 响应状态码当前请求是成功还是失败 状态码信息给页面的提示信息 返回的数据给前端响应的数据字符串、对象、集合
定义在一个实体类Result来包含以上信息。代码如下
public class Result {private Integer code;//响应码1 代表成功; 0 代表失败private String msg; //响应码 描述字符串private Object 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(){return new Result(1,success,null);}//查询 成功响应(把查询结果做为返回数据响应给前端)public static Result success(Object data){return new Result(1,success,data);}//失败响应public static Result error(String msg){return new Result(0,msg,null);}
}
改造Controller
RestController
public class ResponseController { //响应统一格式的结果RequestMapping(/hello)public Result hello(){System.out.println(Hello World ~);//return new Result(1,success,Hello World ~);return Result.success(Hello World ~);}//响应统一格式的结果RequestMapping(/getAddr)public Result getAddr(){Address addr new Address();addr.setProvince(广东);addr.setCity(深圳);return Result.success(addr);}//响应统一格式的结果RequestMapping(/listAddr)public Result listAddr(){ListAddress list new ArrayList();Address addr new Address();addr.setProvince(广东);addr.setCity(深圳);Address addr2 new Address();addr2.setProvince(陕西);addr2.setCity(西安);list.add(addr);list.add(addr2);return Result.success(list);}
}
使用Postman测试 三、分层解耦
3.1 三层架构
在我们进行程序设计以及程序开发时尽可能让每一个接口、类、方法的职责更单一些单一职责原则。 单一职责原则一个类或一个方法就只做一件事情只管一块功能。 这样就可以让类、接口、方法的复杂度更低可读性更强扩展性更好也更利用后期的维护。 我们之前开发的程序呢并不满足单一职责原则。下面我们来分析下下面的程序 那其实我们上述案例的处理逻辑呢从组成上看可以分为三个部分 数据访问负责业务数据的维护操作包括增、删、改、查等操作。 逻辑处理负责业务逻辑处理的代码。 请求处理、响应数据负责接收页面的请求给页面响应数据。
按照上述的三个组成部分在我们项目开发中呢可以将代码分为三层 Controller控制层。接收前端发送的请求对请求进行处理并响应数据。 Service业务逻辑层。处理具体的业务逻辑。 Dao数据访问层(Data Access Object)也称为持久层。负责数据访问操作包括数据的增、删、改、查。
基于三层架构的程序执行流程 前端发起的请求由Controller层接收Controller响应数据给前端 Controller层调用Service层来进行逻辑处理Service层处理完后把处理结果返回给Controller层 Serivce层调用Dao层逻辑处理过程中需要用到的一些数据要从Dao层获取 Dao层操作文件中的数据Dao拿到的数据会返回给Service层 思考按照三层架构的思想如何要对业务逻辑(Service层)进行变更会影响到Controller层和Dao层吗 答案不会影响。 程序的扩展性、维护性变得更好了 3.2 分层解耦
解耦解除耦合。
耦合问题
首先需要了解软件开发涉及到的两个概念内聚和耦合。 内聚软件中各个功能模块内部的功能联系。 耦合衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则高内聚低耦合。 高内聚指的是一个模块中各个元素之间的联系的紧密程度如果各个元素(语句、程序段)之间的联系程度越高则内聚性越高即 高内聚。 低耦合指的是软件中各个层、模块之间的依赖关联程序越低越好。 程序中高内聚的体现 EmpServiceA类中只编写了和员工相关的逻辑处理代码
程序中耦合代码的体现 把业务类变为EmpServiceB时需要修改controller层中的代码 高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。 解耦思路
之前我们在编写代码时需要什么对象就直接new一个就可以了。 这种做法呢层与层之间代码就耦合了当service层的实现变了之后 我们还需要修改controller层的代码。 首先不能在EmpController中使用new对象。代码如下 此时就存在另一个问题了不能new就意味着没有业务层对象程序运行就报错怎么办呢 我们的解决思路是 提供一个容器容器中存储一些对象(例EmpService对象) controller程序从容器中获取EmpService类型的对象
我们想要实现上述解耦操作就涉及到Spring中的两个核心概念 控制反转 Inversion Of Control简称IOC。对象的创建控制权由程序自身转移到外部容器这种思想称为控制反转。 对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为IOC容器或Spring容器 依赖注入 Dependency Injection简称DI。容器为应用程序提供运行时所依赖的资源称之为依赖注入。 程序运行时需要某个资源此时容器就为其提供这个资源。 例EmpController程序运行时需要EmpService对象Spring容器就为其提供并注入EmpService对象
IOC容器中创建、管理的对象称之为bean对象
3.3 IOCDI
上面我们引出了Spring中IOC和DI的基本概念下面我们就来具体学习下IOC和DI的代码实现。
IOCDI入门
任务完成Controller层、Service层、Dao层的代码解耦 思路 删除Controller层、Service层中new对象的代码 Service层及Dao层的实现类交给IOC容器管理 为Controller及Service注入运行时依赖的对象 Controller程序中注入依赖的Service层对象 Service程序中注入依赖的Dao层对象
第1步删除Controller层、Service层中new对象的代码 第2步Service层及Dao层的实现类交给IOC容器管理 使用Spring提供的注解Component 就可以实现类交给IOC容器管理
第3步为Controller及Service注入运行时依赖的对象 使用Spring提供的注解Autowired 就可以实现程序运行时IOC容器自动注入需要的依赖对象
完整的三层代码 Controller层
RestController
public class EmpController {Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量private EmpService empService ;RequestMapping(/listEmp)public Result list(){//1. 调用service, 获取数据ListEmp empList empService.listEmp();//3. 响应数据return Result.success(empList);}
}
Service层
Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class EmpServiceA implements EmpService {Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量private EmpDao empDao ;Overridepublic ListEmp listEmp() {//1. 调用dao, 获取数据ListEmp empList empDao.listEmp();//2. 对数据进行转换处理 - gender, jobempList.stream().forEach(emp - {//处理 gender 1: 男, 2: 女String gender emp.getGender();if(1.equals(gender)){emp.setGender(男);}else if(2.equals(gender)){emp.setGender(女);}//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导String job emp.getJob();if(1.equals(job)){emp.setJob(讲师);}else if(2.equals(job)){emp.setJob(班主任);}else if(3.equals(job)){emp.setJob(就业指导);}});return empList;}
}
Dao层
Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class EmpDaoA implements EmpDao {Overridepublic ListEmp listEmp() {//1. 加载并解析emp.xmlString file this.getClass().getClassLoader().getResource(emp.xml).getFile();System.out.println(file);ListEmp empList XmlParserUtils.parse(file, Emp.class);return empList;}
}
IOC详解
通过IOC和DI的入门程序呢我们已经基本了解了IOC和DI的基础操作。接下来呢我们学习下IOC控制反转和DI依赖注入的细节。
bean的声明
前面我们提到IOC控制反转就是将对象的控制权交给Spring的IOC容器由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。
在之前的入门案例中要把某个对象交给IOC容器管理需要在类上添加一个注解Component
而Spring框架为了更好的标识web应用程序开发当中bean对象到底归属于哪一层又提供了Component的衍生注解 Controller 标注在控制层类上 Service 标注在业务层类上 Repository 标注在数据访问层类上
修改入门案例代码 Controller层 RestController //RestController Controller ResponseBody
public class EmpController {Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量private EmpService empService ;RequestMapping(/listEmp)public Result list(){//1. 调用service, 获取数据ListEmp empList empService.listEmp();//3. 响应数据return Result.success(empList);}
} Service层
Service
public class EmpServiceA implements EmpService {Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量private EmpDao empDao ;Overridepublic ListEmp listEmp() {//1. 调用dao, 获取数据ListEmp empList empDao.listEmp();//2. 对数据进行转换处理 - gender, jobempList.stream().forEach(emp - {//处理 gender 1: 男, 2: 女String gender emp.getGender();if(1.equals(gender)){emp.setGender(男);}else if(2.equals(gender)){emp.setGender(女);}//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导String job emp.getJob();if(1.equals(job)){emp.setJob(讲师);}else if(2.equals(job)){emp.setJob(班主任);}else if(3.equals(job)){emp.setJob(就业指导);}});return empList;}
} Dao层
Repository
public class EmpDaoA implements EmpDao {Overridepublic ListEmp listEmp() {//1. 加载并解析emp.xmlString file this.getClass().getClassLoader().getResource(emp.xml).getFile();System.out.println(file);ListEmp empList XmlParserUtils.parse(file, Emp.class);return empList;}
}
要把某个对象交给IOC容器管理需要在对应的类上加上如下注解之一
注解说明位置ControllerComponent的衍生注解标注在控制器类上ServiceComponent的衍生注解标注在业务类上RepositoryComponent的衍生注解标注在数据访问类上由于与mybatis整合用的少Component声明bean的基础注解不属于以上三类时用此注解
在IOC容器中每一个Bean都有一个属于自己的名字可以通过注解的value属性指定bean的名字。如果没有指定默认为类名首字母小写。 注意事项: 声明bean的时候可以通过value属性指定bean的名字如果没有指定默认为类名首字母小写。 使用以上四个注解都可以声明bean但是在springboot集成web开发中声明控制器bean只能用Controller。 组件扫描
问题使用前面学习的四个注解声明的bean一定会生效吗
答案不一定。原因bean想要生效还需要被组件扫描
下面我们通过修改项目工程的目录结构来测试bean对象是否生效 运行程序后报错 为什么没有找到bean对象呢 使用四大注解声明的bean要想生效还需要被组件扫描注解ComponentScan扫描 ComponentScan注解虽然没有显式配置但是实际上已经包含在了引导类声明注解 SpringBootApplication 中默认扫描的范围是SpringBoot启动类所在包及其子包。 解决方案手动添加ComponentScan注解指定要扫描的包 仅做了解不推荐 推荐做法如下图 将我们定义的controllerservicedao这些包呢都放在引导类所在包com.itheima的子包下这样我们定义的bean就会被自动的扫描到 DI详解
上一小节我们讲解了控制反转IOC的细节接下来呢我们学习依赖注解DI的细节。
依赖注入是指IOC容器要为应用程序去提供运行时所依赖的资源而资源指的就是对象。
在入门程序案例中我们使用了Autowired这个注解完成了依赖注入的操作而这个Autowired翻译过来叫自动装配。
Autowired注解默认是按照类型进行自动装配的去IOC容器中找某个类型的对象然后完成注入操作 入门程序举例在EmpController运行的时候就要到IOC容器当中去查找EmpService这个类型的对象而我们的IOC容器中刚好有一个EmpService这个类型的对象所以就找到了这个类型的对象完成注入操作。 那如果在IOC容器中存在多个相同类型的bean对象会出现什么情况呢 程序运行会报错 如何解决上述问题呢Spring提供了以下几种解决方案 Primary Qualifier Resource
使用Primary注解当存在多个相同类型的Bean注入时加上Primary注解来确定默认的实现。 使用Qualifier注解指定当前要注入的bean对象。 在Qualifier的value属性中指定注入的bean的名称。 Qualifier注解不能单独使用必须配合Autowired使用 使用Resource注解是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。