• SpringBoot Web请求响应


    前言

    之前我们基于SpringBoot的方式开发了一个web应用,浏览器发起请求 /hello 后 ,给浏览器返回字符串 “Hello World ~”。

    在这里插入图片描述

    其实是我们在浏览器发起请求,请求了我们的后端web服务器(也就是内置的Tomcat)。而我们在开发web程序时呢,定义了一个控制器类Controller,请求会被部署在Tomcat中的Controller接收,然后Controller再给浏览器一个响应,响应一个字符串 “Hello World”。 而在请求响应的过程中是遵循HTTP协议的。

    其实在Tomcat这类Web服务器中,是不识别我们自己定义的Controller的。但是Tomcat是一个Servlet容器,是支持Serlvet规范的,所以呢,在tomcat中是可以识别 Servlet程序的。 那我们所编写的XxxController 是如何处理请求的,又与Servlet之间有什么联系呢?

    其实呢,在SpringBoot进行web程序开发时,它内置了一个核心的Servlet程序DispatcherServlet,称之为 核心控制器。 DispatcherServlet 负责接收页面发送的请求,然后根据执行的规则,将请求再转发给后面的请求处理器Controller,请求处理器处理完请求之后,最终再由DispatcherServlet给浏览器响应数据。

    请添加图片描述

    那将来浏览器发送请求,会携带请求数据,包括:请求行、请求头;请求到达tomcat之后,tomcat会负责解析这些请求数据,然后呢将解析后的请求数据会传递给Servlet程序的HttpServletRequest对象,那也就意味着 HttpServletRequest 对象就可以获取到请求数据。 而Tomcat,还给Servlet程序传递了一个参数 HttpServletResponse,通过这个对象,我们就可以给浏览器设置响应数据 。




    请求

    本章节主要讲解如何接收页面传递过来的请求数据。

    Postman

    由于前后端分离,对我们后端技术人员来讲,在开发过程中,是没有前端页面的,那我们怎么测试自己所开发的程序呢?

    方式1:像之前SpringBoot入门案例中一样,直接使用浏览器。在浏览器中输入地址,测试后端程序。

    弊端:在浏览器地址栏中输入地址这种方式都是GET请求,如何我们要用到POST请求怎么办呢?
    要解决POST请求,需要程序员自己编写前端代码(比较麻烦)
    
    • 1
    • 2

    方式2:使用专业的接口测试工具(我们使用Postman工具)

    Postman使用

    在这里插入图片描述

    Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
    可以模拟浏览器向后端服务器发起任何形式(如:get、post)的HTTP请求。
    使用Postman还可以在发起请求时,携带一些请求参数、请求头等信息。

    登录之后可以创建工作空间:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    创建请求:

    在这里插入图片描述

    点击"Save",可以保存当前请求

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述




    简单参数

    简单参数:在向服务器发起请求时,向服务器传递的是一些普通的请求数据。

    在这里插入图片描述

    在后端程序中,如何接收传递过来的普通参数数据呢?
    在这里讲解两种方式:

    1. 原始方式
    2. SpringBoot方式

    原始方式接收普通参数

    这种方式仅做了解,开发不常用。

    在原始的Web程序当中,需要通过Servlet中提供的API:HttpServletRequest(请求对象),获取请求的相关信息。比如获取请求参数:

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

    在Controller中,我们要想获取Request对象,可以直接在方法的形参中声明
    HttpServletRequest 对象。然后就可以通过该对象来获取请求信息:

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

    请添加图片描述

    请添加图片描述



    SpringBoot方式接收普通参数

    在Springboot的环境中,对原始的API进行了封装,接收参数的形式更加简单。 如果是简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数。

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

    postman测试( GET 请求):

    请添加图片描述

    postman测试( POST请求 ):

    请添加图片描述

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




    参数名不一致问题 @RequestParam

    如果方法形参名称与请求参数名称不一致,controller方法中的形参还能接收到请求参数值吗?

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

    答案:运行没有报错。 controller方法中的username值为:null,age值为20

    结论:对于简单参数来讲,请求参数名和controller方法中的形参名不一致时,无法接收到请求
    数据

    那么如果我们开发中,遇到了这种请求参数名和controller方法中的形参名不相同,怎么办?
    解决方案:可以使用Spring提供的@RequestParam注解完成映射。
    在方法形参前面加上 @RequestParam 然后通过value属性执行请求参数名,从而完成映射。代码如下:

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

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

    在这里插入图片描述

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

    @RequestMapping("/simpleParam")
        public String simpleParam(@RequestParam(name = "name", required =
                false) String username, Integer age){
            System.out.println(username+ ":" + age);
            return "OK";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6



    实体参数

    在使用简单参数做为数据传递方式时,前端传递了多少个请求参数,后端controller方法中的形参就要书写多少个。如果请求参数比较多,通过上述的方式一个参数一个参数的接收,会比较繁琐。

    此时,我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:
    请求参数名与实体类的属性名相同

    在这里插入图片描述

    简单实体参数

    定义实体类:

    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 +
                    '}';
        }
    }
    
    • 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

    Controller方法:

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

    Postman测试参数名和实体类属性名一致时正常输出,参数名和实体类属性名不一致时,不一致的参数名输出的是null。



    复杂实体对象

    复杂实体对象指的是,在实体类中有一个或多个属性,也是实体对象类型的。如下:
    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;
        }
    
        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
    
    • 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

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

    Controller方法:

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

    Postman测试:

    在这里插入图片描述




    数组集合参数

    数组集合参数的使用场景:在HTML的表单中,有一个表单项是支持多选的(复选框),可以提交选择的多个值。

    在这里插入图片描述

    实多个值也是一个一个的提交:

    在这里插入图片描述

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

    1. 数组
    2. 集合

    数组参数

    数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数

    在这里插入图片描述

    Controller方法:

    @RestController
    public class RequestController {
        //数组集合参数
        @RequestMapping("/arrayParam")
        public String arrayParam(String[] hobby){
            System.out.println(Arrays.toString(hobby));
            return "OK";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Postman测试:
    在前端请求时,有两种传递形式:
    方式一: xxxxxxxxxx?hobby=game&hobby=java

    在这里插入图片描述

    方式二:xxxxxxxxxxxxx?hobby=game,java

    在这里插入图片描述



    集合参数 @RequestParam

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

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

    在这里插入图片描述

    Controller方法:

    @RestController
    public class RequestController {
        //数组集合参数
        @RequestMapping("/listParam")
        public String listParam(@RequestParam List<String> hobby){
            System.out.println(hobby);
            return "OK";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Postman测试:
    方式一: xxxxxxxxxx?hobby=game&hobby=java

    在这里插入图片描述

    方式二:xxxxxxxxxxxxx?hobby=game,java

    在这里插入图片描述




    日期参数

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

    在这里插入图片描述

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

    在这里插入图片描述

    @DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
    后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
    
    • 1
    • 2

    Controller方法:

    @RestController
    public class RequestController {
        //数组集合参数
        @RequestMapping("/listParam")
        public String listParam(@RequestParam List<String> hobby) {
            System.out.println(hobby);
            return "OK";
        }
    
        //日期时间参数
        @RequestMapping("/dateParam")
        public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
            System.out.println(updateTime);
            return "OK";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Postman测试:

    在这里插入图片描述




    JSON参数 @RequestBody

    在前后端进行交互时,如果是比较复杂的参数,前后端通过使用JSON格式的数据进行传输。 (JSON是开发中最常用的前后端数据交互方式)

    Postman如何发送JSON格式数据:

    在这里插入图片描述

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

    1.传递json格式的参数,在Controller中会使用实体类进行封装。
    2.封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用@RequestBody标识。

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

    在这里插入图片描述

    上图实体类省略了GET , SET 方法。Controller方法:

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

    Postman测试:

    在这里插入图片描述

    经测试GET和POST命令都可以,但是Postman上的数据类型必须从text改成json:

    请添加图片描述




    路径参数 @PathVariable

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

    在这里插入图片描述

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

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

    上述的这种传递请求参数的形式称之为:路径参数。

    前端:通过请求URL直接传递参数
    后端:使用{…}来标识该路径参数,需要使@PathVariable获取路径参数
    
    • 1
    • 2

    在这里插入图片描述

    Controller方法:

    @RestController
    public class RequestController {
        //路径参数
        @RequestMapping("/path/{id}")
        public String pathParam(@PathVariable Integer id){
            System.out.println(id);
            return "OK";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Postman测试:

    在这里插入图片描述




    传递多个路径参数:
    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";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Postman:

    在这里插入图片描述




    响应

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

    @ResponseBody

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

    在这里插入图片描述

    controller方法中的return的结果,怎么就可以响应给浏览器呢?因为我们使用了@ResponseBody注解。

    但是在我们所书写的Controller中,只在类上添加了@RestController注解、方法添加了@RequestMapping注解,并没有使用@ResponseBody注解,怎么给浏览器响应呢?

    我们前面说过@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

    @ResponseBody注解:

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


    下面我们来测试下响应数据:

    @RestController
    public class ResponseController {
        //响应字符串
        @RequestMapping("/hello")
        public String hello(){
            System.out.println("Hello World ~");
            return "Hello World ~";
        }
        //响应实体对象
        @RequestMapping("/getAddr")
        public Address getAddr(){
            Address addr = new Address();//创建实体类对象
            addr.setProvince("广东");
            addr.setCity("深圳");
            return addr;
        }
        //响应集合数据
        @RequestMapping("/listAddr")
        public List<Address> listAddr(){
            List<Address> 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 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

    在服务端响应了一个对象或者集合,那私前端获取到的数据是什么样子的呢?我们使用postman发送请求来测试下。测试效果如下,可以看到返回的是JSON:

    在这里插入图片描述

    在这里插入图片描述




    统一响应结果

    前面所编写的这些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);
        }
    }
    
    • 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

    改造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(){
            List<Address> 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);
        }
    }
    
    • 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

    Postman测试:

    在这里插入图片描述

    在这里插入图片描述




    请求响应案例

    通过一个案例,来加强对请求响应的学习

    案例需求与准备工作

    需求:加载并解析xml文件中的数据,完成数据处理,并在页面展示

    在这里插入图片描述

    案例准备:

    1. XML文件
      已经准备好(emp.xml),直接导入进来,放在 src/main/resources目录下
    2. 工具类
      已经准备好解析XML文件的工具类,无需自己实现
      直接在创建一个包 com.itheima.utils ,然后将工具类拷贝进来
    3. 前端页面资源
      已经准备好,直接拷贝进来,放在src/main/resources下的static目录下

    Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath:/static、 classpath:/public、 classpath:/resources

    在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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    案例实现

    1. 在pom.xml文件中引入dom4j的依赖,用于解析XML文件
    <dependency>
    <groupId>org.dom4jgroupId>
    <artifactId>dom4jartifactId>
    <version>2.1.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.引入资料中提供的:解析XML的工具类XMLParserUtils、实体类Emp、XML文件emp.xml

    在这里插入图片描述

    3.引入资料中提供的静态页面文件,放在resources下的static目录下

    在这里插入图片描述

    1. 创建EmpController类,编写Controller程序,处理请求,响应数据

    在这里插入图片描述

    Contriller代码:

    @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

    ps:

    String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
    这个语句用到了类加载器(getClassLoader())来获取emp.xml,什么原理呢?
    
    在Java的resources文件夹src/main/resources文件夹下的内容会被打包到生成的JAR(或者WAR)文件中,因此这些资源文件会被ClassLoader加载。
    
    在我们的代码中,this.getClass().getClassLoader().getResource("emp.xml").getFile() 这句代码的作用是通过类加载器获取emp.xml文件的路径。
    这里的getClassLoader()方法是java.lang.Class类的一个方法,用于获取类的ClassLoader。
    getResource("emp.xml")方法会在类路径下查找名为emp.xml的文件,并返回一个表示该资源的URL对象。
    最后,getFile()方法获取该URL的文件表示形式,即文件的路径。
    
    由于emp.xml位于resources文件夹下,它会被编译到类路径中。因此,通过类加载器就可以获取到这个文件的路径。
    这种方式通常用于获取类路径下的配置文件、模板文件等资源。
    当应用程序打包成可执行的JAR文件时,这种方式仍然适用,因为resources文件夹中的内容会被包含在JAR文件中,ClassLoader可以正确地加载这些资源。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13


    XmlParserUtils的代码是老师写的提供的,也贴在这里,以供学习

    package com.example.springbootwebquickstart1.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;
        }
    
    }
    
    • 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

    测试与问题

    代码编写完毕,运行引导类,启动服务进行测试了。Postman测试:

    在这里插入图片描述

    打开浏览器,在浏览器地址栏输入:localhost:8080/emp.html

    在这里插入图片描述

    上述案例的功能,虽然已经实现,但是解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。

    在这里插入图片描述

    当前程序的这个业务逻辑还是比较简单的,如果业务逻辑再稍微复杂一点,我们会看到Controller方法的代码量就很大了。

    当我们要修改操作数据部分的代码,需要改动Controller
    当我们要完善逻辑处理部分的代码,需要改动Controller
    当我们需要修改数据响应的代码,还是需要改动Controller
    
    • 1
    • 2
    • 3

    这样呢,就会造成我们整个工程代码的复用性比较差,而且代码难以维护。
    其实在现在的开发中,有非常成熟的解决思路,那就是分层开发




  • 相关阅读:
    Spring之@Repository @Service @Component @Controller的区别和使用场景
    《2022谷歌开发者大会》参会之旅
    Nautilus Chain 与 Coin98 生态达成合作,加速 Zebec 生态亚洲战略进
    Int数据取byte,Byte数据取bit
    常见的基础算法以及技巧框架
    谈谈 JVM 垃圾回收机制
    Xpath注入学习记录
    WindowsAPI 进程和线程相关说明
    Zookeeper集群安装部署、Kafka集群安装部署
    PyTorch 入门
  • 原文地址:https://blog.csdn.net/weixin_43739821/article/details/133982316