• 【SpringBoot】请求与响应参数 IoC与DI 总结



    ① —— 请求 ——

    一、简单参数 @RequestParam

    1.1 参数与形参 命名相同

    参数名与形参变量名相同,定义形参即可接收参数。

    @RestController
    @RequestMapping("hello")
    public class HelloController {
    
        @GetMapping("show")
        public String show(String name,String age){
            System.out.println(name+"! Hello World! !" + age);
            return name+" Hello World! " + age ;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1
    参数名或形参名不同 传入传出数值为空Null
    1
    1

    1.2 参数与形参 命名不同

    使用注解 @RequestParam 当形参名字不同时,绑定与参数对应的新名字!通过注解完成映射!

    // name : 命名
    // required : 默认为TRUE 即必须有;FALSE允许没有即为可以空Null
    @RequestParam(name = "age",required = false) String age
    
    • 1
    • 2
    • 3

    挂载注解后,默认required默认为TRUE,即必须传参,否则报错!
    1

    二、实体参数

    @Data
    public class User {
        private String name;
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.1 简单实体对象

        @GetMapping("show1")
        public String show1(User user){
            System.out.println(user);
            return user.toString();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1
    1

    • 参数名和实体类属性名需要一致 : 否则属性为空Null

    2.2 复杂实体对象

    Address 实体类:

    @Data
    public class Address {
        private String province;
        private String city;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • User类中有一个Address类型的属性(Address是一个实体类)
    • 请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。
    • 1

    contoller:

        @GetMapping("show1")
        public String show1(User user){
            System.out.println(user);
            return user.toString();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1

    三、数组集合参数

    数组集合参数的使用场景:在HTML的表单中,有一个表单项是支持多选的(复选框),可以提交选择的多个值。
    1
    多个值是怎么提交的呢?其实多个值也是一个一个的提交。
    在这里插入图片描述

    后端程序接收上述多个值的方式有两种:

    1. 数组
    2. 集合

    3.1 数组

    controller

        @GetMapping("show2")
        public String show2(String[] hobby){
            System.out.println(Arrays.toString(hobby));
            return Arrays.toString(hobby);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1

    3.2 集合 @RequestParam

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

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

        @GetMapping("show3")
        public String show3(@RequestParam List<String> hobby){
            System.out.println(hobby);
            return hobby.toString();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1

    四、日期参数 @DateTimeFormat

    在一些特殊的需求中,可能会涉及到日期类型数据的封装:
    1

    因为日期的格式多种多样(如:2023-12-12 10:05:45 、2023/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式

        @GetMapping("show4")
        public String show4(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
            System.out.println(updateTime);
            return updateTime.toString();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • @DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
    • 后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
      1

    五、JSON参数 @RequestBody

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

    • 传递json格式的参数,在Controller中会使用实体类进行封装。
    • 封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。
    • 1
    • @RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)

    controller

        @PostMapping("list")
        public String list(@RequestBody User user){
            System.out.println(user);
            return user.toString();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1

    六、路径参数 @PathVariable

    路径参数:直接在请求的URL中传递参数:
    http://localhost:8080/hello/path/wake/20
    1

        @GetMapping("path/{name}/{id}")
        public String path(@PathVariable String name,@PathVariable Integer id){
            System.out.println(name+" "+id);
            return name+" "+id;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1

    七、请求头参数 @RequestHeader

        @GetMapping("head")
        public String head(@RequestHeader String name){
            System.out.println(name);
            return name;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1


    ② —— 响应 ——

    八、@ResponseBody

    • 类型:方法注解、类注解
    • 位置:写在Controller方法上或类上
    • 作用:将方法返回值直接响应给浏览器
      • 如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器

    1

    直接在上加 @RestController 即可 :
    @RestController = @Controller + @ResponseBody

    @RestController源码:

    @Target({ElementType.TYPE})   //元注解(修饰注解的注解)
    @Retention(RetentionPolicy.RUNTIME)  //元注解
    @Documented    //元注解
    @Controller   
    @ResponseBody 
    public @interface RestController {
        @AliasFor(
            annotation = Controller.class
        )
        String value() default "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    类上有@RestController注解或@ResponseBody注解时:

    • 表示当前类下所有的方法返回值做为响应数据

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

    测试:

    • 回显对象数据
        @GetMapping("show1")
        public User show1(User user){
            System.out.println(user);
            user.setAge(99);
            return user;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1

    • 回显集合数据
        @PostMapping("list")
        public List<User> list(@RequestBody User user){
            List<User> list = new ArrayList<>();
            user.setName("DougWake");
            list.add(user);
            return list;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1

    九、统一响应结果(Result工具类)

    前面所编写的这些Controller方法中,返回值各种各样,没有任何的规范:
    1在真实的项目开发中,无论是哪种方法,我们都会定义一个统一的返回结果。方案如下:
    1

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

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

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

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

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

    9.1 Result工具类代码:

    Result工具类:

    package com.wake.utils;
    
    /**
     * 全局统一返回结果类
     */
    public class Result<T> {
        // 返回码
        private Integer code;
        // 返回消息
        private String message;
        // 返回数据
        private T data;
        public Result(){}
        // 返回数据
        protected static <T> Result<T> build(T data) {
            Result<T> result = new Result<T>();
            if (data != null)
                result.setData(data);
            return result;
        }
        public static <T> Result<T> build(T body, Integer code, String message) {
            Result<T> result = build(body);
            result.setCode(code);
            result.setMessage(message);
            return result;
        }
        public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
            Result<T> result = build(body);
            result.setCode(resultCodeEnum.getCode());
            result.setMessage(resultCodeEnum.getMessage());
            return result;
        }
        /**
         * 操作成功
         * @param data  baseCategory1List
         * @param 
         * @return
         */
        public static<T> Result<T> ok(T data){
            Result<T> result = build(data);
            return build(data, ResultCodeEnum.SUCCESS);
        }
        public Result<T> message(String msg){
            this.setMessage(msg);
            return this;
        }
        public Result<T> code(Integer code){
            this.setCode(code);
            return this;
        }
        public Integer getCode() {
            return code;
        }
        public void setCode(Integer code) {
            this.code = code;
        }
        public String getMessage() {
            return message;
        }
        public void setMessage(String message) {
            this.message = message;
        }
        public T getData() {
            return data;
        }
        public void setData(T data) {
            this.data = data;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    Result 响应码 枚举类:

    package com.wake.utils;
    
    /**
     * 统一返回结果状态信息类
     */
    public enum ResultCodeEnum {
    
        SUCCESS(200, "success"),
        USERNAME_ERROR(501, "usernameError"),
        PASSWORD_ERROR(503, "passwordError"),
        NOTLOGIN(504, "notLogin"),
        USERNAME_USED(505, "userNameUsed");
    
        private Integer code;
        private String message;
    
        private ResultCodeEnum(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    返回统一的响应结果:
    在这里插入图片描述

    9.2 静态资源

    资源地址可以在yml配置文件中修改,改完就直接覆盖默认路径。

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

    • classpath:/static/
    • classpath:/public/
    • classpath:/resources/
    • classpath:/META-INF/resources/

    classpath:

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

    【SpringBoot3】整合SpringMVC_静态资源处理

    前端资源放这了:
    1

    十、案例测试 (解析xml前端显示数据)

    10.1 解析xml文件

    dom4j的依赖,用于解析XML文件:

    <dependency>
        <groupId>org.dom4jgroupId>
        <artifactId>dom4jartifactId>
        <version>2.1.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解析XML的工具类XMLParserUtils:

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    10.2 加载解析xml 数据转换处理

    案例:读取xml 加载数据转换解析 显示在前端页面

    实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Emp {
        private String name;
        private Integer age;
        private String image;
        private String gender;
        private String job;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    controller:

    @RestController
    public class EmpController {
        @RequestMapping("/listEmp")
        public Result list(){
            //1. 加载并解析emp.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
            empList.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("就业指导");
                }
            });
            //3. 响应数据
            return Result.success(empList);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    http://localhost:8080/emp.html :
    1

    10.3 问题分析

    我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。
    1

    十一、分层解耦

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

    • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。Dao/Mapper
    • 逻辑处理:负责业务逻辑处理的代码。service
    • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。controller

    11.1 三层架构

    1

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

    基于三层架构的程序执行流程:
    1

    • 前端发起的请求,由Controller层接收(Controller响应数据给前端)
    • Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
    • Service层进行业务处理(MD5加密等),调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
    • Dao层与数据库相关联(Dao拿到的数据会返回给Service层)
      1

    11.2 分层解耦

    【Java】面向对象 OOP_OOP三大特性之一:封装_为什么需要封装
    1

    程序中高内聚的体现:
    1
    程序中耦合代码的体现:
    1

    高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。
    1

    11.3 解耦思路

    1
    不能在EmpController中使用new对象。

    • 提供一个容器,容器中存储一些对象(例:EmpService对象)
    • controller程序从容器中获取EmpService类型的对象

    11.4 IoC & DI

    • 控制反转:
      • Inversion Of Control,简称IOC。
      • 对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
      • 这个容器称为:IOC容器或Spring容器
    • 依赖注入:
      • Dependency Injection,简称DI。
      • 容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
      • 程序运行时需要某个资源,此时容器就为其提供这个资源。
      • 例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象

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

    使用IOC容器 把需要的资源 装进去,使用DI将容器内的资源拿来用!

    【Spring】IoC容器 控制反转 与 DI依赖注入 概念 第一期
    【Spring】IoC容器 控制反转 与 DI依赖注入 三种实现方式 总结 第五期

    11.4.1 IoC 详解

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

    其实底层都是@Component注解
    在这里插入图片描述

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

    注意事项:

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

    11.4.2 组件扫描

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

    • 模拟测试
      1
      运行程序后,报错:
      1
      使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描

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

    • 也可以:手动添加@ComponentScan注解,指定要扫描的包(不建议)
      1
    • 推荐做法:
    • 将我们定义的controller,service,dao这些包,都放在引导类所在包com.wake下:这样我们定义的bean就会被自动的扫描到
    • 1

    11.4.3 DI 详解

    @Autowired 注解:

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

    如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?
    1
    程序报错:显示需要一个单一Bean,但是存在两个。
    1
    如何解决上述问题呢?Spring提供了以下几种解决方案:

    • @Primary

    • @Qualifier

    • @Resource

    1. @Primary注解
      确定默认的实现
      1
    2. @Qualifier注解
    • 指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。
    • @Qualifier注解不能单独使用,必须配合@Autowired使用
      1
    1. @Resource注解
    • 是按照bean的名称进行注入。
    • 通过name属性指定要注入的bean的名称。
      1

    面试题 : @Autowird 与 @Resource的区别

    • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
    • @Autowired 默认是按照类型注入,而@Resource是按照名称注入

    总结

    使用自动构建SpringBoot 报错!
    1

    错误: 找不到或无法加载主类 com.wake.SpringbootTestPartApplication 原因: java.lang.ClassNotFoundException: com.wake.SpringbootTestPartApplication

    解决: maven

    clean 
    comlile
    
    • 1
    • 2
  • 相关阅读:
    探索 Linux 命令 `yum`:软件包管理器的奥秘
    java计算机毕业设计ssm物流快递管理系统
    Essential C++ 编程基础
    Netty学习(二)-- 概述和初体验
    拥抱数字正义时代,看AIGC如何驱动法律变革
    SSM+健身房管理系统 毕业设计-附源码191656
    Java的Socket Timeout和tcp的存活探测包是不是一个东西
    Spring Security(3)
    ubuntu18.04 开机后黑屏,左上角光标(强制关机后)
    从 0 开始手写实现一个 RPC 框架!
  • 原文地址:https://blog.csdn.net/GavinGroves/article/details/136769635