当前位置:   article > 正文

请求与响应_应用请求方与应用响应方

应用请求方与应用响应方

在SpringBoot进行web程序开发时,它内置了一个核心的Servlet程序 DispatcherServlet,称之为 核心控制器。 DispatcherServlet 负责接收页面发送的请求(HttpServletRequest 对象就可以获取到请求数据),然后根据执行的规则,将请求再转发给后面的请求处理器Controller,请求处理器处理完请求之后,最终再由DispatcherServlet给浏览器响应数据(通过HttpServletResponse这个对象,我们就可以给浏览器设置响应数据)

BS架构

Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。

请求

如何接收页面传递过来的请求数据。

测试时可以使用专业的接口测试工具(如Postman工具)

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。

1.接收传递过来的普通参数数据

 两种方式:

  • 原始方式

  • SpringBoot方式

原始方式(了解)

Tomcat接收到http请求时:把请求的相关信息封装到HttpServletRequest对象中

  1. //根据指定的参数名获取请求参数的数据值
  2. String  request.getParameter("参数名")
  3. @RestController
  4. public class RequestController {
  5.    //原始方式
  6.    @RequestMapping("/simpleParam")
  7.    public String simpleParam(HttpServletRequest request){
  8.        // http://localhost:8080/simpleParam?name=Tom&age=10
  9.        // 请求参数: name=Tom&age=10   (有2个请求参数)
  10.        // 第1个请求参数: name=Tom   参数名:name,参数值:Tom
  11.        // 第2个请求参数: age=10     参数名:age , 参数值:10
  12.        String name = request.getParameter("name");//name就是请求参数名
  13.        String ageStr = request.getParameter("age");//age就是请求参数名
  14.        int age = Integer.parseInt(ageStr);//需要手动进行类型转换
  15.        System.out.println(name+" : "+age);
  16.        return "OK";
  17.   }
  18. }

 SpringBoot方式

 简单参数,定义同名的形参即可接收参数。

  1. @RestController
  2. public class RequestController {
  3.    // http://localhost:8080/simpleParam?name=Tom&age=10
  4.    // 第1个请求参数: name=Tom   参数名:name,参数值:Tom
  5.    // 第2个请求参数: age=10     参数名:age , 参数值:10
  6.    
  7.    //springboot方式
  8.    @RequestMapping("/simpleParam")
  9.    public String simpleParam(String name , Integer age ){//形参名和请求参数名保持一致
  10.        System.out.println(name+" : "+age);
  11.        return "OK";
  12.   }
  13. }

结论:不论是GET请求还是POST请求,对于简单参数来讲,只要保证请求参数名和Controller方法中的形参名保持一致,就可以获取到请求参数中的数据值。

:如果方法形参名称与请求参数名称不一致运行没有报错。对应值为null,无法接收到请求数据

可以使用Spring提供的@RequestParam注解完成映射

  1. @RestController
  2. public class RequestController {
  3.    // http://localhost:8080/simpleParam?name=Tom&age=20
  4.    // 请求参数名:name
  5.    //springboot方式
  6.    @RequestMapping("/simpleParam")
  7.    public String simpleParam(@RequestParam("name") String username , Integer age ){
  8.        System.out.println(username+" : "+age);
  9.        return "OK";
  10.   }
  11. }

注意事项:

@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错

如果该参数是可选的,可以将required属性设置为false

simpleParam(@RequestParam(name = "name", required = false) String username, Integer age)

 2.实体参数

请求参数比较多时,将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守请求参数名与实体类的属性名相同

简单实体对象

  1. //实体类
  2. public class User {
  3.    private String name;
  4.    private Integer age;
  5.    public String getName() {
  6.        return name;
  7.   }
  8.    public void setName(String name) {
  9.        this.name = name;
  10.   }
  11.    public Integer getAge() {
  12.        return age;
  13.   }
  14.    public void setAge(Integer age) {
  15.        this.age = age;
  16.   }
  17.    @Override
  18.    public String toString() {
  19.        return "User{" +
  20.                "name='" + name + '\'' +
  21.                ", age=" + age +
  22.                '}';
  23.   }
  24. }
  25. //Controller方法:
  26. @RestController
  27. public class RequestController {
  28.    //实体参数:简单实体对象
  29.    @RequestMapping("/simplePojo")
  30.    public String simplePojo(User user){
  31.        System.out.println(user);
  32.        return "OK";
  33.   }
  34. }

参数名和实体类属性名不一致时,为null

3. 复杂实体对象

复杂实体对象:在实体类中有一个或多个属性,也是实体对象类型的。

(请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。)

先定义POJO实体类

  1. @RestController
  2. public class RequestController {
  3. //实体参数:复杂实体对象
  4. @RequestMapping("/complexPojo")
  5. public String complexPojo(User user){
  6. System.out.println(user);
  7. return "OK";
  8. }
  9. }

4.数组集合参数

情景:提交多个值(复选框)

后端程序接收上述多个值的方式有两种:数组和集合

数组参数

  1. //数组集合参数,提交多个值(后端程序用数组或者集合接收)
  2. // 数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
  3. @RequestMapping("/arrayParam")
  4. //地址表示方式一: xx?hobby=game&hobby=java
  5. //方式二:xx?hobby=game,java
  6. public String arrayParam(String[] hobby)
  7. {
  8. System.out.println(Arrays.toString(hobby));
  9. return "ok";
  10. }

集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系

默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系

  1. @RequestMapping("/listParam")
  2. public String list(@RequestParam List<String> hobby)
  3. {
  4. System.out.println(hobby);
  5. return "ok";
  6. }

5 日期参数

涉及到日期类型数据的封装,通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。前端的日期参数必须按照指定的格式传递。

后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。

  1. @RestController
  2. public class RequestController {
  3. //日期时间参数
  4. @RequestMapping("/dateParam")
  5. public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
  6. System.out.println(updateTime);
  7. return "OK";
  8. }
  9. }

6.JSON参数

 (JSON是开发中最常用的前后端数据交互方式,使用JSON格式的数据进行传输。)

Postman在发送请求时传递json格式的请求参数

服务端Controller方法接收JSON格式数据

  • 使用实体类进行封装。

  • JSON数据键名与形参对象属性名相同,定义实体类类型形参即可接收参数。需要使用 @RequestBody标识。

  • @RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)

  1. @RestController
  2. public class RequestController {
  3. //JSON参数
  4. @RequestMapping("/jsonParam")
  5. public String jsonParam(@RequestBody User user){
  6. System.out.println(user);
  7. return "OK";
  8. }
  9. }

7.路径参数

传统的开发中请求参数是放在请求体(POST请求)传递或跟在URL后面通过?key=value的形式传递(GET请求)。

在现在的开发中,经常还会直接在请求的URL中传递参数

http://localhost:8080/user/1		
http://localhost:880/user/1/0

这种传递请求参数的形式:路径参数。

路径参数:

  • 前端:通过请求URL直接传递参数

  • 后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数

  1. @RestController
  2. public class RequestController {
  3. //路径参数
  4. @RequestMapping("/path/{id}")
  5. public String pathParam(@PathVariable Integer id){
  6. System.out.println(id);
  7. return "OK";
  8. }
  9. //传递多个路径参数
  10. @RequestMapping("/path/{id}/{name}")
  11. public String pathParam2(@PathVariable Integer id, @PathVariable String name){
  12. System.out.println(id+ " : " +name);
  13. return "OK";
  14. }
  15. }

响应

Controller程序呢,除了接收请求外,还可以进行响应。

在我们前面所编写的controller方法中,都已经设置了响应数据。

controller方法中的return的结果,通过使用@ResponseBody注解可以响应给浏览器

@ResponseBody注解:

  • 位置:书写在Controller方法上或类上

  • 作用:将方法返回值直接响应给浏览

  • @RestController = @Controller + @ResponseBody,在类上添加@RestController就相当于添加了@ResponseBody注解。

  • 类上有@RestController注解或@ResponseBody注解时:表示当前类下所有的方法返回值做为响应数据

  • 方法的返回值,如果是一个实体类对象或集合时,会先转换为JSON格式,在响应给浏览器

统一响应结果

前端:只需要按照统一格式的返回结果进行解析(仅一种解析方案),就可以拿到数据。

统一的返回结果使用类来描述,在这个结果中包含:

  • 响应状态码:当前请求是成功,还是失败

  • 状态码信息:给页面的提示信息

  • 返回的数据:给前端响应的数据(字符串、对象、集合)

  1. //实体类保存统一的响应结果
  2. public class Result {
  3. private Integer code;//响应码,1 代表成功; 0 代表失败
  4. private String msg; //响应码 描述字符串
  5. private Object data; //返回的数据
  6. public Result() { }
  7. public Result(Integer code, String msg, Object data) {
  8. this.code = code;
  9. this.msg = msg;
  10. this.data = data;
  11. }
  12. //gets方法和sets方法
  13. //增删改 成功响应(不需要给前端返回数据)
  14. public static Result success(){
  15. return new Result(1,"success",null);
  16. }
  17. //查询 成功响应(把查询结果做为返回数据响应给前端)
  18. public static Result success(Object data){
  19. return new Result(1,"success",data);
  20. }
  21. //失败响应
  22. public static Result error(String msg){
  23. return new Result(0,msg,null);
  24. }
  25. }
  26. 改造Controller
  27. @RestController
  28. public class ResponseController {
  29. //响应统一格式的结果
  30. @RequestMapping("/hello")
  31. public Result hello(){
  32. System.out.println("Hello World ~");
  33. //return new Result(1,"success","Hello World ~");
  34. return Result.success("Hello World ~");
  35. }
  36. //响应统一格式的结果
  37. @RequestMapping("/listAddr")
  38. public Result listAddr(){
  39. List<Address> list = new ArrayList<>();
  40. Address addr = new Address();
  41. addr.setProvince("广东");
  42. addr.setCity("深圳");
  43. Address addr2 = new Address();
  44. addr2.setProvince("陕西");
  45. addr2.setCity("西安");
  46. list.add(addr);
  47. list.add(addr2);
  48. return Result.success(list);
  49. }
  50. }

案例

需求

准备工作

  1. XML文件导入进来,放在 src/main/resources目录下

  2. 准备好解析XML文件的工具类,将工具类拷贝进来

  3. 前端页面资源,放在src/main/resources下的static目录下

在SpringBoot项目中,静态资源默认可以存放的目录:

  • classpath:/static/

  • classpath:/public/

  • classpath:/resources/

  • classpath:/META-INF/resources/

classpath:

  • 代表的是类路径,在maven的项目中,其实指的就是 src/main/resources 或者 src/main/java,但是java目录是存放java代码的,所以相关的配置文件及静态资源文档,就放在 src/main/resources下。

  1. @RestController
  2. public class EmpController {
  3. @RequestMapping("/listEmp")
  4. public Result list(){
  5. //1. 加载并解析emp.xml
  6. String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
  7. //System.out.println(file);
  8. List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
  9. //2. 对数据进行转换处理 - gender, job
  10. empList.stream().forEach(emp -> {
  11. //处理 gender 1: 男, 2: 女
  12. String gender = emp.getGender();
  13. if("1".equals(gender)){
  14. emp.setGender("男");
  15. }else if("2".equals(gender)){
  16. emp.setGender("女");
  17. }
  18. //处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
  19. String job = emp.getJob();
  20. if("1".equals(job)){
  21. emp.setJob("讲师");
  22. }else if("2".equals(job)){
  23. emp.setJob("班主任");
  24. }else if("3".equals(job)){
  25. emp.setJob("就业指导");
  26. }
  27. });
  28. //3. 响应数据
  29. return Result.success(empList);
  30. }
  31. }

在浏览器地址栏输入: http://localhost:8080/emp.html测试

分层解耦

三层架构

单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。

这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。

在项目开发中呢,可以将代码分为三层:

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据

  • Service:业务逻辑层。处理具体的业务逻辑

  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

    1. //控制层:接收前端发送的请求,对请求进行处理,并响应数据
    2. @RestController
    3. public class EmpController {
    4. //业务层对象
    5. private EmpService empService = new EmpServiceA();
    6. @RequestMapping("/listEmp")
    7. public Result list(){
    8. //1. 调用service层, 获取数据
    9. List<Emp> empList = empService.listEmp();
    10. //3. 响应数据
    11. return Result.success(empList);
    12. }
    13. }
    14. //业务逻辑层:处理具体的业务逻辑
    15. //业务逻辑接口(制定业务标准)
    16. public interface EmpService {
    17. //获取员工列表
    18. public List<Emp> listEmp();
    19. }
    20. //业务逻辑实现类(按照业务标准实现)
    21. public class EmpServiceA implements EmpService {
    22. //dao层对象
    23. private EmpDao empDao = new EmpDaoA();
    24. @Override
    25. public List<Emp> listEmp() {
    26. //1. 调用dao, 获取数据
    27. List<Emp> empList = empDao.listEmp();
    28. //2. 对数据进行转换处理 - gender, job
    29. empList.stream().forEach(emp -> {
    30. //处理 gender 1: 男, 2: 女
    31. String gender = emp.getGender();
    32. if("1".equals(gender)){
    33. emp.setGender("男");
    34. }else if("2".equals(gender)){
    35. emp.setGender("女");
    36. }
    37. //处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
    38. String job = emp.getJob();
    39. if("1".equals(job)){
    40. emp.setJob("讲师");
    41. }else if("2".equals(job)){
    42. emp.setJob("班主任");
    43. }else if("3".equals(job)){
    44. emp.setJob("就业指导");
    45. }
    46. });
    47. return empList;
    48. }
    49. }
    50. //数据访问层:负责数据的访问操作,包含数据的增、删、改、查
    51. //数据访问层接口(制定标准)
    52. public interface EmpDao {
    53. //获取员工列表数据
    54. public List<Emp> listEmp();
    55. }
    56. 数据访问实现类
    57. //数据访问实现类
    58. public class EmpDaoA implements EmpDao {
    59. @Override
    60. public List<Emp> listEmp() {
    61. //1. 加载并解析emp.xml
    62. String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
    63. System.out.println(file);
    64. List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
    65. return empList;
    66. }
    67. }

分层解耦

  • 内聚:软件中各个功能模块内部的功能联系。

  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。

软件设计原则:高内聚低耦合。

不在类里new一个其他类对象而是提供一个容器,容器中存储一些对象

controller程序从容器中获取EmpService类型的对象

其中有两个核心概念IOC和DI

  • 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。

    对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器

  • 依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。

    程序运行时需要某个资源,此时容器就为其提供这个资源。

    例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象

IOC容器中创建、管理的对象,称之为:bean对象

IOC&DI

入门

任务:完成Controller层、Service层、Dao层的代码解耦

第1步:删除Controller层、Service层中new对象的代码

第2步:Service层及Dao层的实现类,交给IOC容器管理

  • 使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理

第3步:为Controller及Service注入运行时依赖的对象

  • 使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象

  1. //Controller层:
  2. @RestController
  3. public class EmpController {
  4. @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
  5. private EmpService empService ;
  6. @RequestMapping("/listEmp")
  7. public Result list(){
  8. //1. 调用service, 获取数据
  9. List<Emp> empList = empService.listEmp();
  10. //3. 响应数据
  11. return Result.success(empList);
  12. }
  13. }
  14. //Service层:
  15. @Component //将当前对象交给IOC容器管理,成为IOC容器的bean
  16. public class EmpServiceA implements EmpService {
  17. @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
  18. private EmpDao empDao ;
  19. @Override
  20. public List<Emp> listEmp() {
  21. //1. 调用dao, 获取数据
  22. List<Emp> empList = empDao.listEmp();
  23. //2. 对数据进行转换处理 - gender, job
  24. empList.stream().forEach(emp -> {
  25. //处理 gender 1: 男, 2: 女
  26. String gender = emp.getGender();
  27. if("1".equals(gender)){
  28. emp.setGender("男");
  29. }else if("2".equals(gender)){
  30. emp.setGender("女");
  31. }
  32. //处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
  33. String job = emp.getJob();
  34. if("1".equals(job)){
  35. emp.setJob("讲师");
  36. }else if("2".equals(job)){
  37. emp.setJob("班主任");
  38. }else if("3".equals(job)){
  39. emp.setJob("就业指导");
  40. }
  41. });
  42. return empList;
  43. }
  44. }
  45. //Dao层:
  46. @Component //将当前对象交给IOC容器管理,成为IOC容器的bean
  47. public class EmpDaoA implements EmpDao {
  48. @Override
  49. public List<Emp> listEmp() {
  50. //1. 加载并解析emp.xml
  51. String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
  52. System.out.println(file);
  53. List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
  54. return empList;
  55. }
  56. }

bean的声明

IOC容器创建的对象称为bean对象。

在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component

@Component的衍生注解:

  • @Controller (标注在控制层类上)

  • @Service (标注在业务层类上)

  • @Repository (标注在数据访问层类上)

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:

注解说明位置
@Controller@Component的衍生注解标注在控制器类上
@Service@Component的衍生注解标注在业务类上
@Repository@Component的衍生注解标注在数据访问类上(由于与mybatis整合,用的少)
@Component声明bean的基础注解不属于以上三类时,用此注解

在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性指定bean的名字。如果没有指定,默认为类名首字母小写。

注意事项:

  • 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。

  • 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

组件扫描

bean想要生效,还需要被组件@ComponentScan扫描

@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,默认扫描的范围是SpringBoot启动类所在包及其子包

bean不生效解决方案:

  • 手动添加@ComponentScan注解,指定要扫描的包 (了解)

  • 将我们定义的controller,service,dao这些包呢,都放在引导类所在包的子包下

 DI

依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象

@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)

如果在IOC容器中,存在多个相同类型的bean对象,程序运行会报错

解决方案:

  • @Primary

  • @Qualifier

  • @Resource

面试题 

@Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号