Web后端-请求响应

黑马程序员JavaWeb开发教程

文章目录

一、请求

1、简单参数

  • 请求参数名与方法形参变量名相同
  • 会自动进行类型转换
  1. 在controller目录下创建新类RequestController,其代码如下

    package com.itheima.conttoller;

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    //测试简单参数
    @RestController
    public class RequestController {
    @RequestMapping("/simpleParam")
    public String simpleParam(String name, Integer age) {
    System.out.println(name + " : " + age);
    return "ok";
    }
    }

  2. 重新启动项目,在postman中输入以下地址:http://localhost:8080/simpleParam 并设置两个参数,name和age,点击send按钮,会看到返回结果

  3. 注意事项:如果方法形参名称与请求参数名称不匹配,可以使用@RequestParam完成映射

  4. 注意事项:@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。如果该参数是可选的,可以将required属性设置为false

2、实体参数

  • 简单实体对象:请求参数名与形参对象属性名相同,定义POJO接收即可
  1. 简单实体参数
  • 新建目录pojo,在pojo目录下新建类User,代码如下

    package com.itheima.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;
      }
    
    
      @Override
      public String toString() {
          return "User{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  '}';
      }

    }

  • 在RequestController类中新增方法simplePojo,代码如下

    @RequestMapping("/simplePojo")
    public String simplePojo(User user){
    System.out.println(user);
    return "OK";
    }

  • 重启项目,在Postman中配置相应的设置

  1. 复杂实体参数
  • 在pojo目录下新建类Address,代码如下

    package com.itheima.pojo;

    public class Address {
    private String province;
    private String city;

    复制代码
      @Override
      public String toString() {
          return "Address{" +
                  "province='" + province + '\'' +
                  ", city='" + 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;
      }

    }

  • 修改User的代码,新增一个属性Address

  • 在RequestController类中新增方法complexPojo,代码如下

    @RequestMapping("/complexPojo")
    public String complexPojo(User user){
    System.out.println(user);
    return "OK";
    }

  • 重启项目,在Postman中设置相应配置

3、数组集合参数

(1)数组参数

  • 数组参数:请求参数名与形参数组名称相同且请求蚕食为多个,定义数组类型形参可接收参数
  1. 在RequestController类中新增一个方法,其代码如下

    @RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby) {
    System.out.println(Arrays.toString(hobby));
    return "OK";
    }

  2. 重启项目,配置postman

(2)集合参数

  • 集合参数:请求参数名与形参集合名称相同且请求参数为多个,@RequestParam 绑定参数关系
  1. 在RequestController类中新增一个方法,其代码如下

    @RequestMapping("/listParam")
    public String listParam(@RequestParam List<String> hobby) {
    System.out.println(hobby);
    return "OK";
    }

  2. 重启项目,配置postman

4、日期参数

  • 日期参数:使用@DateTimeFormat注解完成日期参数格式转换
  1. 在RequestController类中新增一个方法,其代码如下

    @RequestMapping("/dateParam")
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
    System.out.println(updateTime);
    return "OK";
    }

  2. 重启项目,配置postman

5、json参数

  • JSON参数:JSON数据见名与形参对象属性名相同,定义POJO类型形参可接受参数,需要使用@RequestBody标识

(1)在Postman中怎么发起请求来传递JSON格式的请求参数

(2)在服务端如何接收JSON格式的数据

  • 一般通过实体对象来接收,JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接受参数,需要使用@RequestBody标识
  1. 在RequestController类中新增一个方法,其代码如下

    @RequestMapping("/jsonParam")
    public String jsonParam(@RequestBody User user) {
    System.out.println(user);
    return "OK";
    }

  2. 重启项目,配置postman

6、路径参数

-路径参数:通过请求URL直接传递参数,使用{...}来标识该路径参数,需要使用@PathVariable获取路径参数

  1. 在RequestController类中新增一个方法,其代码如下

    @RequestMapping("/pathParam/{id}")
    public String pathParam(@PathVariable Integer id) {
    System.out.println(id);
    return "OK";
    }

  2. 重启项目,配置postman

  3. 可以传递多个路径参数,使用/分隔开即可

7、总结

  1. 简单参数
    • 定义方法形参,请求参数名与形参变量名一致
    • 如果不一致,使用@RequestParam手动映射
  2. 实体参数
    • 请求参数名,与实体对象的属性名一致,会自动封装
  3. 数组集合参数
    • 数组:请求参数名与数组名一致,会自动接收封装
    • 集合:请求参数名与集合名一致,@RequestParam绑定关系
  4. 日期参数
    • @DateFormat
  5. JSON参数
    • @RequestBody
  6. 路径参数
    • @PathVariable

二、响应

(1)@ResponseBody

  1. 类型:方法注解、类注解
  2. 位置:Controller方法上/类上
  3. 作用:将方法返回值直接响应,如果返回值类型是实体对象/集合,将会转换为JSON格式响应
  4. 说明:@RequestController+@RequestBody

(2)统一响应结果

  1. 在pojo目录下新增类Result,代码如下

    package com.itheima.pojo;

    /**

    • 统一响应结果封装类
      */
      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 +
      '}';
      }
      }

  2. 之后所有的方法都返回Result类型的数据,新建TestController类代码如下

    package com.itheima.conttoller;

    import com.itheima.pojo.Address;
    import com.itheima.pojo.Result;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.ArrayList;
    import java.util.List;

    @RestController
    public class TestController {
    @RequestMapping("/helloworld")
    public Result hello() {
    System.out.println("Hello world~");
    return Result.success("OK");
    }

    复制代码
     @RequestMapping("/getAddr")
     public Result getAddr() {
         Address addr = new Address();
         addr.setProvince("广东");
         addr.setCity("深圳");
         return Result.success(addr);
     }
    
     @RequestMapping("/listAddr")
     public Result listAddr() {
         List<Address> list = new ArrayList<>();
         Address addr = new Address();
         addr.setProvince("广东");
         addr.setCity("深圳");
    
         Address addr1 = new Address();
         addr1.setProvince("陕西");
         addr1.setCity("西安");
    
         list.add(addr);
         list.add(addr);
         return Result.success(list);
    
     }

    }

  3. 在postman中配置测试

(3)案例

  • 获取员工数据,返回统一响应结果,在页面渲染展示
  1. 加载并解析emp.xml文件中的数据,完成数据处理,并在页面展示
  2. 步骤(操作在详解中解释)
    • 在pom.xml文件中引入dom4j的依赖,用于解析xml文件
    • 引入解析XML的工具类XMLParserUtils、对应的实体类Emp、XML文件emp.xml
    • 引入静态页面文件,放在resources下的static目录下
    • 编写Controller程序,处理请求,响应数据
案例详解
  1. 在pom.xml文件中引入dom4j的依赖,用于解析xml文件。
  • dom4j的坐标

    <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency>
  1. 引入解析XML的工具类XMLParserUtils、对应的实体类Emp、XML文件emp.xml

(1)引入解析XML的工具类XMLParserUtils

XmlParserUtils的代码如下:

复制代码
package com.itheima.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

Emp类的代码如下

复制代码
package com.itheima.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)引入XML文件emp.xml

  • 将emp.xml文件放到resources目录下

    emp.xml文件中的代码如下:

    <?xml version="1.0" encoding="UTF-8" ?> <emps> <emp> <name>金毛狮王</name> <age>55</age> <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/1.jpg</image> <gender>1</gender> <job>1</job> </emp>
    复制代码
      <emp>
          <name>白眉鹰王</name>
          <age>65</age>
          <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/2.jpg</image>
          <gender>1</gender>
          <job>1</job>
      </emp>
    
      <emp>
          <name>青翼蝠王</name>
          <age>45</age>
          <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/3.jpg</image>
          <gender>1</gender>
          <job>2</job>
      </emp>
    
      <emp>
          <name>紫衫龙王</name>
          <age>38</age>
          <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/4.jpg</image>
          <gender>2</gender>
          <job>3</job>
      </emp>
    </emps>
  1. 引入静态页面文件,放在resources下的static目录下(前端静态页面存放在资料中)

  • Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath://static、classpath://public、classpath://resources。其中的classpath指的就是resources文件夹
    (1)另外,想要返回同意结果,还需要将Result.java放到pojo目录下

    Result的代码文章上方有
  1. 编写Controller程序,处理请求,响应数据
    (1)在controller目录下,新建类EmpController 用于加载员工数据

    EmpController中的代码:

    package com.itheima.controller;

    import com.itheima.pojo.Emp;
    import com.itheima.pojo.Result;
    import com.itheima.utils.XmlParserUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    @RestController
    public class EmpController {
    @RequestMapping("/listEmp")//路径,看前端页面emp.html中的钩子方法
    public Result list() {//前端页面emp.html的钩子方法没有返回数据,也就不需要有参数
    // 1、加载并解析xml文件
    // (1)获得xml文件地址
    String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
    System.out.println("emp.xml文件地址:" + file);
    // (2)使用之前引入的工具类XmlParserUtils解析xml文件
    List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
    // 2、对数据进行转换(将1,2转换成男女等)
    for (Emp emp : empList) {
    // (1)处理gender- 1:男, 2:女
    String gender = emp.getGender();
    if ("1".equals(gender)) {
    emp.setGender("男");
    } else if ("2".equals(gender)) {
    emp.setGender("女");
    }
    // (2)处理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("就业指导");
    }
    }

    复制代码
         // 3、响应数据
         return Result.success(empList);
     }

    }

  2. 结果
    (1)postman响应结果

    (2)浏览器访问前端页面结果:http://localhost:8080/emp.html

最终项目结构

三、分层解耦

1、三层架构

(1)三层结构分别是

  1. controller:控制层,接受前段发送的请求,队请求进行处理,并相应数据
  2. service:业务逻辑层,处理具体的业务逻辑
  3. dao:数据访问层,负责数据访问操作,包括数据的增删改查

(2)三层架构当中请求的执行流程

(3)修改之前的统一响应数据案例为三层架构

1. Dao层的代码
  • Dao层访问的可能是文件当中的数据,数据库当中的数据,或者是别人给我们提供的一个接口当中的数据。实现的方式有很多,要想灵活的去切换各种实现,可以选择面向接口的编程方式,在编写到层的实现之前,先编写一个到的接口来增强程序的灵活和扩展性。
  1. 在dao目录下创建接口类EmpDao

    其中的代码:

    package com.itheima.dao;

    import com.itheima.pojo.Emp;

    import java.util.List;

    public interface EmpDao {
    //获取员工列表数据
    public List<Emp> listEmp();
    }

  2. 在dao目录下新建impl目录,在impl目录下新建EmpDaoA类,继承接口EmpDao

    其中代码

    package com.itheima.dao.impl;

    import com.itheima.dao.EmpDao;
    import com.itheima.pojo.Emp;
    import com.itheima.utils.XmlParserUtils;

    import java.util.List;

    public class EmpDaoA implements EmpDao {
    @Override
    public List<Emp> listEmp() {
    // 1、加载并解析xml文件
    // (1)获得xml文件地址
    String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
    System.out.println("emp.xml文件地址:" + file);
    // (2)使用之前引入的工具类XmlParserUtils解析xml文件
    List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
    return empList;
    }
    }

2. service层的代码
  1. 为增强灵活性创建一个service层的接口EmpService

    其中的代码:

    package com.itheima.service;

    import com.itheima.pojo.Emp;

    import java.util.List;

    public interface EmpService {
    //获取员工列表
    public List<Emp> listEmp();
    }

  2. 在service目录下新建impl目录,在impl目录下新建EmpServiceA类

    EmpServiceA类代码

    package com.itheima.service.impl;

    import com.itheima.dao.EmpDao;
    import com.itheima.dao.impl.EmpDaoA;
    import com.itheima.pojo.Emp;
    import com.itheima.service.EmpService;

    import java.util.List;

    public class EmpServiceA implements EmpService {
    private EmpDao empDao = new EmpDaoA();

    复制代码
     @Override
     public List<Emp> listEmp() {
         List<Emp> empList = empDao.listEmp();
         // 2、对数据进行转换(将1,2转换成男女等)
         for (Emp emp : empList) {
             // (1)处理gender- 1:男, 2:女
             String gender = emp.getGender();
             if ("1".equals(gender)) {
                 emp.setGender("男");
             } else if ("2".equals(gender)) {
                 emp.setGender("女");
             }
             // (2)处理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;
     }

    }

3.Controller层的代码
  1. 使用过service获得逻辑处理之后的数据

    package com.itheima.controller;

    import com.itheima.pojo.Emp;
    import com.itheima.pojo.Result;
    import com.itheima.service.EmpService;
    import com.itheima.service.impl.EmpServiceA;
    import com.itheima.utils.XmlParserUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    @RestController
    public class EmpController {
    private EmpService empService= new EmpServiceA();
    @RequestMapping("/listEmp")//路径,看前端页面emp.html中的钩子方法
    public Result list() {//前端页面emp.html的钩子方法没有返回数据,也就不需要有参数
    //使用service获得处理之后的数据
    List<Emp> empList = empService.listEmp();
    // 3、响应数据
    return Result.success(empList);
    }
    }

4.之后使用postman、浏览器访问都获得与之前相同的结果说明正确

2、 分层解耦

  • 内聚:软件中各个功能模块内部的功能联系
  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度
  • 软件设计原则:高内聚低耦合
  • 控制反转:Inversion of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器)这种思想称为控制反转
  • 依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入
  • Bean对象:IOC容器中创建、管理的对象,称之为Bean

3、IOC&DI入门

(1)解耦

  1. 要将controller层和service层解耦,service层和Dao层解耦,就是将所有的new删除

(2)步骤

1. service层及Dao层的实现类,交给IOC容器管理
  1. 将EmpController中的new EmpServiceA删除

  2. 将EmpServiceA中的new EmpDaoA删除

  3. 在EmpServiceA和EmpDaoA的前边加上注解@Component

2. 为Controller及Service注入运行时,依赖的对象
  1. 在EmpController和EmpService中加入注解@Autowired
3. 运行测试

4、IOC详解

(1)bean的声明

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

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

    • 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写
    • 使用以上四个注解都可以生命bean,但是在springboot继承web开发中,声明控制器bean只能用@Controller

(2)bean组件扫描

  1. 前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描
  2. @ComponentScan注解虽然没有显示配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包

5、DI详解

(1)存在多个类型相同的bean时的解决方案

  1. @Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean,将会报错

  2. 可以通过一下几种方案来解决

    • @Primary:在想要使用的bean的注解前边加上@Primary注解,再次运行就不会出现报错了

    • @Autowired+@Qualifier("bean的名称")

    • @Resource(name="bean的名称")

(2)@Resource与@Autowired的区别

  1. @Autowired是spring框架提供的注解,而@Resource是JDK提供的注解
  2. @Autowired默认按照类型注入,而@Resource默认是按照名称注入
相关推荐
坐吃山猪2 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫3 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao3 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区4 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT5 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy5 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss7 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续7 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0447 小时前
ReAct模式解读
java·ai