• 59、SpringBoot 自定义JSON的序列化器和反序列化器


    Serialization(序列化):java对象以一连串的字节码保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。

    deserialization(反序列化): 将保存在磁盘文件中的java字节码重新转换成java对象称为反序列化。

    ★ 自定义JSON的序列化器和反序列化器

    ▲ 注册自定义序列化器和反序列化器有两种方式:
    
    - 方式1:利用Jackson的模块机制来注册自定义序列化器和反序列化器。
    
    - 方式2:利用Spring Boot提供的@JsonComponent来注册自定义序列化器和反序列化器。
    
    第一种方式是Jackson原生的注册方式,一般不推荐。推荐使用第二种方式。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ★ 使用@JsonComponent注册序列化器或反序列化器

    Spring Boot提供了@JsonComponent注解来注册自定义自定义序列化器和反序列化器,
    该注解有两种使用方式:

    -方式1: 直接使用 @JsonComponent 注解修饰JsonSerializer、JsonDeserializer或KeyDeserializer实现类,
      这些实现类将会由Spring Boot注册为自定义JSON序列化器和反序列化器。
      @JsonComponent 注解修饰 JsonSerializer,就是自定义 JSON 序列化器
      @JsonComponent 注解修饰 JsonDeserializer,就是自定义 JSON 反序列化器
      
    
    -方式2:使用 @JsonComponent 修饰包含JsonSerializer/JsonDeserializer内部实现类的外部类,
      这些内部实现类将会由Spring Boot注册为自定义JSON序列化器和反序列化器。
      这种方式相当于将序列化器和反序列化器都定义在一个外部类中,这种方式具有更好的内聚性,
      通常推荐用方式2。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类

    Token定义
    Token是Fastjson中定义的json字符串的同类型字段,即"{“、”["、数字、字符串等,用于分隔json字符串不同字段。
    例如,{“姓名”:“张三”,“年龄”:“20”}是一个json字符串,在反序列化之前,需要先将其解析为
    { 、 姓名、 :、 张三、 ,、 年龄、 :、 20、 }这些字段的Token流,随后再根据class反序列化为响应的对象。
    在进行Token解析之前,json字符串对程序而言只是一个无意义的字符串。需要将json字符串解析为一个个的Token,并以Token为单位解读json数据。

    代码演示:

    自定义序列化和反序列化器的代码

    package cn.ljh.app.custom;
    
    
    import cn.ljh.app.domain.Book;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import lombok.SneakyThrows;
    import org.springframework.beans.BeanUtils;
    import org.springframework.boot.jackson.JsonComponent;
    
    import java.io.IOException;
    
    //内部类就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类,这个 CustomSeDe 就是外部类
    //使用 @JsonComponent 修饰包含JsonSerializer/JsonDeserializer内部实现类的外部类,
    //这些内部实现类将会由Spring Boot注册为自定义JSON序列化器和反序列化器。
    @JsonComponent
    public class CustomSeDe
    {
        //在该类中可定义自定义的序列化器和反序列化器
    
    
        //postman 用get请求时走这个方法
        //自定义序列化器,这个就是内部类
        public static class MySerializer extends JsonSerializer<Book>
        {
    
            @Override
            public void serialize(Book book, JsonGenerator jsonGenerator,
                                  SerializerProvider serializerProvider) throws IOException
            {
                //在该方法中完成自定义的序列化输出,将对象输出成 json 或者是 xml 字符串
    
                jsonGenerator.writeStartObject(); //输出 {  这个花括号 , 输出对象用这个括号
    //            jsonGenerator.writeStartArray();  //输出 [ 这个中括号  , 输出数值用这个括号
                jsonGenerator.writeNumberField("id", book.getId());
                //将book对象的name属性,序列化为title 属性
                jsonGenerator.writeStringField("title", book.getName());
                jsonGenerator.writeNumberField("price", book.getPrice());
                jsonGenerator.writeStringField("author", book.getAuthor());
    //            jsonGenerator.writeEndArray();   // 输出这个 ] 中括号
                jsonGenerator.writeEndObject();  //输出这个 } 花括号
            }
        }
    
        //postman 用post的请求访问到这个方法
        //自定义反序列化器,这个就是内部类
        public static class MyDeserializer extends JsonDeserializer<Book>
        {
            //这个方法负责反序列化,就是将 json 或者是 xml 字符串 恢复成对象
            @SneakyThrows
            @Override
            public Book deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException
            {
    
                Book book = new Book();
    
                //JsonToken采用顺序方式依次读取 JSON 或 XML 字符串中的每个 token
                //token:一个花括号也叫token,一个符号隔开一个内容就叫token
                JsonToken jsonToken = jsonParser.getCurrentToken();
                //只要这个 Json 或者 xml 字符串没有结束,都应该继续向下读取
                while (!jsonToken.equals(JsonToken.END_OBJECT))
                {
                    //这个循环的作用,是去保证要找到代表 Field Name 的 Token
                    //如果当前读取的Token 不是字段名
                    if (!jsonToken.equals(JsonToken.FIELD_NAME))
                    {
                        jsonToken = jsonParser.nextToken();//读取下一个token
                        continue;
                    }
                    //只要程序进到此处,就一定是读取到 field name 这种字段token
                    //从 JSON 或者是 XML 字符串中读取得到的字段名,也就是Book 对象的属性名
                    String fieldName = jsonParser.getCurrentName();
                    //读取属性名之后,下一个Token就是属性值了
                    JsonToken valueToken = jsonParser.nextToken();
                    //获取当前token的值
                    String value = jsonParser.getText();
                    //对于要逻辑处理的属性,可以在这里进行判断处理
                    if (fieldName.equals("title"))
                    {
                        if (!value.startsWith("《"))
                        {
                            value = "《" + value;
                        }
                        if (!value.endsWith("》"))
                        {
                            value += "》";
                        }
                        //完成了对Book对象的name属性的设置
                        book.setName(value);
                    } else if (fieldName.equals("price"))
                    {
                        double price = Double.parseDouble(value);
                        //把从json或xml格式的价格数据拿出来,然后打折set进去
                        book.setPrice(price * 0.5);
                    } else
                    {
                        //对于其他属性,不需要额外处理的话,直接通过反射来调用Book对象的fieldName对应的setter方法
                        BeanUtils.getPropertyDescriptor(Book.class, fieldName)
                                .getWriteMethod()
                                .invoke(book, value);
                    }
                    //继续向下读取下一个 Token
                    jsonToken = jsonParser.nextToken();
                }
                return book;
            }
        }
    
    }
    
    • 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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    测试:

    序列化输出,将对象输出成 json 或者是 xml 字符串
    反序列化,就是将 json 或者是 xml 字符串 恢复成对象

    前端发送 get 请求,会走序列化方法。
    http://localhost:8080/books
    (不知道是如何定义访问的方法是走序列化还是反序列化方法)
    get 请求是获取对象,序列化方法,把对象序列化成json格式的数据返回
    在这里插入图片描述

    序列化输出,将对象输出成 json 或者是 xml 字符串
    反序列化,就是将 json 或者是 xml 字符串 恢复成对象

    用 post 请求,会走反序列化方法,
    发送的请求是 json 格式的数据,所以走反序列化方法,将数据恢复成对象
    在这里插入图片描述

    不太懂如何怎么应用这个自定义的序列化和反序列化器。

    完整代码:

    Book

    在这里插入图片描述

    BookController

    package cn.ljh.app.controller;
    
    
    import cn.ljh.app.domain.Book;
    import cn.ljh.app.service.BookService;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    /*
     *     GET  /books/{id} - (获取数据) 获取指定id的图书
     *     GET  /books?参数  -(获取数据) 获取符合查询参数的图书
     *     GET  /books        -(获取数据) 获取所有图书
     *     POST /books        -(添加数据) 添加图书
     *     PUT  /books/{id}    -(更新数据) 更新指定ID的图书
     *     DELETE /books/{id}    -(删除数据) 删除指定ID的图书
     *     DELETE /books?参数    -(删除数据) 删除符合指定参数的图书
     *
     *  Restful处理方法的返回值通常都应该使用HttpEntity或ResponseEntity。
     *
     */
    
    @RequestMapping("/books")
    @RestController
    public class BookController
    {
        //有参构造器进行依赖注入
        private BookService bookService;
    
        public BookController(BookService bookService)
        {
            this.bookService = bookService;
        }
    
    
        //根据id查看图书
        @GetMapping("/{id}")
        public ResponseEntity<Book> viewBookById(@PathVariable Integer id)
        {
            Book book = bookService.getBookById(id);
    
            //参数1:响应数据体  参数2:需要添加的响应头,没有就给个null   参数3:响应码 , OK 代表 200
            return new ResponseEntity<>(book, null, HttpStatus.OK);
        }
    
        //查看所有图书
        @GetMapping("")
        public ResponseEntity<List<Book>> viewBooks()
        {
            List<Book> allBooks = bookService.getAllBooks();
    
            return new ResponseEntity<>(allBooks, null, HttpStatus.OK);
        }
    
        //添加图书
        @PostMapping("")
        public ResponseEntity<Book> addBook(@RequestBody Book book)
        {
            Book b = bookService.addOrUpdateBook(book);
            //HttpStatus.CREATED 代表返回的状态码为 201
            return new ResponseEntity<>(b, null, HttpStatus.CREATED);
        }
    
        //根据id更新图书信息
        @PutMapping("/{id}")
        public ResponseEntity<Book> updateBookById(@PathVariable Integer id, @RequestBody Book book)
        {
            book.setId(id);
            Book b = bookService.addOrUpdateBook(book);
    
            return new ResponseEntity<>(b, null, HttpStatus.OK);
        }
    
        //根据id删除图书
        @DeleteMapping("/{id}")
        public ResponseEntity<Book> deleteBookById(@PathVariable Integer id)
        {
            Book book = bookService.deleteBookById(id);
            return new ResponseEntity<>(book, null, HttpStatus.OK);
        }
    
    }
    
    • 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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    BookService

    package cn.ljh.app.service;
    
    import cn.ljh.app.domain.Book;
    
    import java.util.List;
    
    public interface BookService
    {
        //根据id查看图书
        Book getBookById(Integer id);
    
        //查看所有图书
        List<Book> getAllBooks();
    
        //添加/修改图书
        Book addOrUpdateBook(Book book);
    
        //根据id删除图书
        Book deleteBookById(Integer id);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    BookServiceImpl

    package cn.ljh.app.service.impl;
    
    import cn.ljh.app.domain.Book;
    import cn.ljh.app.service.BookService;
    import org.springframework.stereotype.Service;
    
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    
    
    @Service
    public class BookServiceImpl implements BookService
    {
        //创建一个线程安全的map集合存数据,假设为数据库
        static Map<Integer, Book> bookDB = new ConcurrentHashMap<>();
        static int nextId = 1;
    
        //初始化数据库的数据
        static
        {
            bookDB.put(nextId, new Book(nextId++, "火影忍者", 120, "岸本"));
            bookDB.put(nextId, new Book(nextId++, "七龙珠", 121, "鸟山明"));
        }
    
    
        //根据id查看图书
        @Override
        public Book getBookById(Integer id)
        {
            if (id != null)
            {
                Book book = bookDB.get(id);
                if (book!=null){
                    return book;
                }
            }
            throw new RuntimeException("根据id查看图书失败!");
        }
    
        //查看所有图书
        @Override
        public List<Book> getAllBooks()
        {
            //获取map中的所有数据
            Collection<Book> mapBooks = bookDB.values();
            //强转
            List<Book> books = new ArrayList<>(mapBooks);
            return books;
        }
    
        //添加/修改图书
        @Override
        public Book addOrUpdateBook(Book book)
        {
            if (book.getId() != null){
                //修改
                //map的key是唯一的,所以map里面有这个key的话,直接把原来的value覆盖掉
                bookDB.put(book.getId(),book);
                return book;
            }else {
                //新增
                //为新增的图书设置id
                book.setId(nextId);
                //book添加完之后,这个id才会自增
                bookDB.put(nextId++,book);
                return book;
            }
        }
    
        //根据id删除图书
        @Override
        public Book deleteBookById(Integer id)
        {
            Book book = bookDB.remove(id);
            return book;
        }
    }
    
    • 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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    CustomSeDe

    package cn.ljh.app.custom;
    
    
    import cn.ljh.app.domain.Book;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import lombok.SneakyThrows;
    import org.springframework.beans.BeanUtils;
    import org.springframework.boot.jackson.JsonComponent;
    
    import java.io.IOException;
    
    //内部类就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类,这个 CustomSeDe 就是外部类
    //使用 @JsonComponent 修饰包含JsonSerializer/JsonDeserializer内部实现类的外部类,
    //这些内部实现类将会由Spring Boot注册为自定义JSON序列化器和反序列化器。
    @JsonComponent
    public class CustomSeDe
    {
        //在该类中可定义自定义的序列化器和反序列化器
    
    
        //postman 用get请求时走这个方法
        //自定义序列化器,这个就是内部类
        public static class MySerializer extends JsonSerializer<Book>
        {
    
            @Override
            public void serialize(Book book, JsonGenerator jsonGenerator,
                                  SerializerProvider serializerProvider) throws IOException
            {
                //在该方法中完成自定义的序列化输出,将对象输出成 json 或者是 xml 字符串
    
                jsonGenerator.writeStartObject(); //输出 {  这个花括号 , 输出对象用这个括号
                //jsonGenerator.writeStartArray();  //输出 [ 这个中括号  , 输出数值用这个括号
                jsonGenerator.writeNumberField("id", book.getId());
                //将book对象的name属性,序列化为title 属性
                jsonGenerator.writeStringField("title", book.getName());
                jsonGenerator.writeNumberField("price", book.getPrice());
                jsonGenerator.writeStringField("author", book.getAuthor());
                //jsonGenerator.writeEndArray();   // 输出这个 ] 中括号
                jsonGenerator.writeEndObject();  //输出这个 } 花括号
            }
        }
    
        //postman 用post的请求访问到这个方法
        //自定义反序列化器,这个就是内部类
        public static class MyDeserializer extends JsonDeserializer<Book>
        {
            //这个方法负责反序列化,就是将 json 或者是 xml 字符串 恢复成对象
            @SneakyThrows
            @Override
            public Book deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException
            {
    
                Book book = new Book();
    
                //JsonToken采用顺序方式依次读取 JSON 或 XML 字符串中的每个 token
                //token:一个花括号也叫token,一个符号隔开一个内容就叫token
                JsonToken jsonToken = jsonParser.getCurrentToken();
                //只要这个 Json 或者 xml 字符串没有结束,都应该继续向下读取
                while (!jsonToken.equals(JsonToken.END_OBJECT))
                {
                    //这个循环的作用,是去保证要找到代表 Field Name 的 Token
                    //如果当前读取的Token 不是字段名
                    if (!jsonToken.equals(JsonToken.FIELD_NAME))
                    {
                        jsonToken = jsonParser.nextToken();//读取下一个token
                        continue;
                    }
                    //只要程序进到此处,就一定是读取到 field name 这种字段token
                    //从 JSON 或者是 XML 字符串中读取得到的字段名,也就是Book 对象的属性名
                    String fieldName = jsonParser.getCurrentName();
                    //读取属性名之后,下一个Token就是属性值了
                    JsonToken valueToken = jsonParser.nextToken();
                    //获取当前token的值
                    String value = jsonParser.getText();
                    //对于要逻辑处理的属性,可以在这里进行判断处理
                    if (fieldName.equals("title"))
                    {
                        if (!value.startsWith("《"))
                        {
                            value = "《" + value;
                        }
                        if (!value.endsWith("》"))
                        {
                            value += "》";
                        }
                        //完成了对Book对象的name属性的设置
                        book.setName(value);
                    } else if (fieldName.equals("price"))
                    {
                        double price = Double.parseDouble(value);
                        //把从json或xml格式的价格数据拿出来,然后打折set进去
                        book.setPrice(price * 0.5);
                    } else
                    {
                        //对于其他属性,不需要额外处理的话,直接通过反射来调用Book对象的fieldName对应的setter方法
                        BeanUtils.getPropertyDescriptor(Book.class, fieldName)
                                .getWriteMethod()
                                .invoke(book, value);
                    }
                    //继续向下读取下一个 Token
                    jsonToken = jsonParser.nextToken();
                }
                return book;
            }
        }
    
    }
    
    • 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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.5</version>
        </parent>
        <groupId>cn.ljh</groupId>
        <artifactId>CustomSeDe</artifactId>
        <version>1.0.0</version>
        <name>CustomSeDe</name>
        <properties>
            <java.version>11</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- 添加 Jackson format XML , 用于对象与XML之间的转换 -->
            <dependency>
                <groupId>com.fasterxml.jackson.dataformat</groupId>
                <artifactId>jackson-dataformat-xml</artifactId>
            </dependency>
    
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
    • 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
  • 相关阅读:
    【三】安装k8s+kuboard, 拉取harbor镜像并执行yml文件
    小波神经网络的基本原理,小波神经网络算法原理
    项目实战-day1.0
    搭个ChatGPT算法模型,离Java程序员有多远?
    猿创征文|JAVA 实现《连连看》游戏
    cesium 实战记录(一) 底图动态配置,前端实现切换
    初识华为云数据库GaussDB for openGauss
    中国人保为正华消防承保产品责任险,为消费者保驾护航!
    AcWing_11. 背包问题求方案数_dp
    【opencv-c++】读取视频文件或者直播码流
  • 原文地址:https://blog.csdn.net/weixin_44411039/article/details/132777837