62、SpringBoot 使用RestTemplate 整合第三方 RESTful 服务

这节的要点:

就是弄两个项目 , 从 端口9090 这个项目,通过 restTemplate, 去访问 端口8080 的项目,并获取8080项目的数据。

★ RESTful服务包含两方面的含义

复制代码
1. 自己的应用要暴露一些功能供别人来调用。
   此时我们是服务端。

2. 我们的应用要去整合第三方的RESTful服务
   此时我们就是客户端。

★ RESTful客户端的两种方式

复制代码
- 应用基于传统的Spring MVC框架,此时考虑使用RestTemplate来整合第三方RESTful服务。
  RestTemplate就属于传统Spring Web的API。

- 应用基于传统的Web Flux框架,此时考虑使用WebClient来整合第三方RESTful服务。
  WebClient本身就是属于WebFlux API

★ RestTemplate整合第三方RESTful服务

复制代码
(1)调用RestTemplateBuilder来构建RestTemplate

     千万不要自己去创建RestTemplate对象,相当于你没有利用spring容器的依赖注入。

(2)调用RestTemplate的如下方法:
     delete | getForXxx | postForXxx | put 

     exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Map<String,?> uriVariables):
     delete | getForXxx | postForXxx | put 都是 exchange的快捷方式

     execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor, Map<String,?> uriVariables):原始

代码演示

RESTful_XML 8080 项目代表 restful 服务的 服务端,生成json响应的

MyRestTemPlate 9090 项目代表 restful 服务的 客户端,发起请求的

需求:两个项目 , 从 9090 这个项目,通过 restTemplate 去访问 8080 的项目,并获取数据。

查看所有图书的方法。

效果:成功通过restTemplate,从项目9090 访问到项目8080的数据

其他的方法也一样

根据id查看图书

根据id修改图书:两种方法,有返回值和没有返回值

方式1: 用 put() 方法,restTemplate.put() 默认没有返回值

方式2:用 exchange() 方法,restTemplate.exchange() 有返回值

完整代码:

MyRestTemPlate

Book

java 复制代码
package cn.ljh.app.domain;

import lombok.Data;

@Data
public class Book
{
    private Integer id;
    private String name;
    private double price;
    private String author;

    public Book(Integer id, String name, double price, String author)
    {
        this.id = id;
        this.name = name;
        this.price = price;
        this.author = author;
    }
}

ClientController

java 复制代码
package cn.ljh.app.controller;


import cn.ljh.app.domain.Book;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.Collection;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/mybooks")
public class ClientController
{
    private final RestTemplate restTemplate;

    public ClientController(RestTemplateBuilder restTemplateBuilder)
    {
        /*
         * 此处的 RestTemplateBuilder 是来自于Spring 容器的注入,
         * 因此它所构建的 RestTemplate 已经接收了spring 容器的默认设置
         * 如果直接创建 RestTemplate , 那就相当于完全没有利用Spring容器的依赖注入,
         * 因此完全不能接受spring容器的默认配置,这样后面所介绍的配置 RestTemplate 完全不可能实现配置了
         */
        this.restTemplate = restTemplateBuilder
                .rootUri("http://192.168.0.108:8080/") //为整个RestTemplate 指定基路径
                .build();

    }

    //查看所有图书
    @GetMapping("/viewBooks")
    public ResponseEntity<List<Book>> viewBooks()
    {
        List<Book> list = restTemplate.getForObject("/books/viewBooks", List.class);
        ResponseEntity<List<Book>> entity = new ResponseEntity<>(list, null, HttpStatus.OK);
        return entity;
    }

    //查看所有图书
    @GetMapping("/viewBooks2")
    public ResponseEntity<Collection> viewBooks2()
    {
        //参数3:代表为 URL 填充路径参数值 ,就是路径后面的参数
        Collection data = restTemplate.getForObject("/books/viewBooks2", Collection.class);
        ResponseEntity<Collection> entity = new ResponseEntity<>(data, null, HttpStatus.OK);
        return entity;
    }


    //根据id查看图书
    @GetMapping("/{id}")
    public ResponseEntity<Book> getBookById(@PathVariable Integer id)
    {
        Book book = restTemplate.getForObject("/books/{id}", Book.class, id);
        ResponseEntity<Book> entity = new ResponseEntity<>(book, null, HttpStatus.OK);
        return entity;
    }

    //根据id删除图书
    @DeleteMapping("/{id}")
    public ResponseEntity<Map<String, String>> deleteBookById(@PathVariable Integer id)
    {
        //restTemplate 的 delete 默认是没有返回值的
        restTemplate.delete("/books/{id}", id);
        ResponseEntity<Map<String, String>> tip = new ResponseEntity<>(Map.of("tip", "id为:" + id + " 的图书删除成功"), null, HttpStatus.OK);
        return tip;
    }

    //根据id修改图书, restTemplate 的 put修改 默认是没有返回值的
    @PutMapping("/{id}")
    public ResponseEntity<Map<String, String>> updateById(@PathVariable Integer id)
    {
        restTemplate.put("/books/{id}",
                Map.of("name", "修改的图书", "price", 120, "author", "图书的作者"),
                id);
        ResponseEntity<Map<String, String>> entity =
                new ResponseEntity<>(Map.of("tip", "图书修改成功"), null, HttpStatus.OK);
        return entity;
    }

    //因为 restTemplate 默认的 put修改、delete 是没有返回值的,如果要返回值,可以用 exchange 方法
    //参数2:代表要修改的数据封装在这个 map 里面
    @PutMapping("/exchange/{id}")
    public ResponseEntity<Map> updateById(@PathVariable Integer id, @RequestBody Map map)
    {
        //把要修改的book对象传进这个 HttpEntity 对象里面
        HttpEntity<Map> entity = new HttpEntity<>(map);
        //参数1:访问服务端的路径  参数2:要执行的请求类型  参数3:请求的数据,就是要修改的对象的新数据
        //参数4:指定服务器响应的数据类型  参数5 :用于为 URL 的路径参数填充值
        ResponseEntity<Map> exchange = restTemplate.exchange("/books/{id}", HttpMethod.PUT, entity, Map.class, id);

        return exchange;

    }


}

application.properties

pom.xml

pom文件没什么要改的

java 复制代码
<?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>my_resttemplate</artifactId>
    <version>1.0.0</version>
    <name>my_resttemplate</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>

        <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>

RESTful_XML

Book

java 复制代码
package cn.ljh.app.domain;

import lombok.Data;

@Data
public class Book
{
    private Integer id;
    private String name;
    private double price;
    private String author;

    public Book(Integer id, String name, double price, String author)
    {
        this.id = id;
        this.name = name;
        this.price = price;
        this.author = author;
    }
}

controller

java 复制代码
package cn.ljh.app.controller;


import cn.ljh.app.domain.Book;
import cn.ljh.app.service.BookService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Collection;
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);
    }

    //查看所有图书----------------------方法1------------
    @GetMapping("viewBooks")
    public ResponseEntity<List<Book>> viewBooks()
    {
        List<Book> allBooks = bookService.getAllBooks();

        return new ResponseEntity<>(allBooks, null, HttpStatus.OK);
    }

    //查看所有图书------------------------方法2----------
    @GetMapping("/viewBooks2")
    public ResponseEntity<Collection<Book>> viewBooks2()
    {
        Collection<Book> allBooks2 = bookService.getAllBooks2();

        ResponseEntity<Collection<Book>> books = new ResponseEntity<>(allBooks2, null, HttpStatus.OK);
        return books;
    }

    //添加图书
    @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);
    }

}

BookService

java 复制代码
package cn.ljh.app.service;

import cn.ljh.app.domain.Book;

import java.util.Collection;
import java.util.List;

public interface BookService
{
    //根据id查看图书
    Book getBookById(Integer id);

    //查看所有图书
    List<Book> getAllBooks();

    Collection<Book> getAllBooks2();

    //添加/修改图书
    Book addOrUpdateBook(Book book);

    //根据id删除图书
    Book deleteBookById(Integer id);

}

BookServiceImpl

java 复制代码
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 Collection<Book> getAllBooks2()
    {
        //获取map中的所有数据
        Collection<Book> mapBooks = bookDB.values();
        return mapBooks;
    }

    //添加/修改图书
    @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;
    }
}

pom.xml

java 复制代码
<?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>RESTful_XML</artifactId>
    <version>1.0.0</version>
    <name>RESTful_XML</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>
相关推荐
uzong25 分钟前
把一面面试总结做成一个 Skill:重复的事,就别每次都重写
后端·面试
霸道流氓气质36 分钟前
SpringBoot中基于 AES-GCM + KMS 密钥管理的数据加解密 Starter 实践
java·数据库·spring boot
程序员海军1 小时前
一个 AI 应用开发程序员的一天,都在屏幕前忙些什么?
前端·后端·程序员
乐观的Terry1 小时前
8、发布系统-完整流水线的核心
java·spring boot·spring·spring cloud
不能放弃治疗1 小时前
MCP
后端
万亿少女的梦1682 小时前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
江湖十年2 小时前
Go 语言中 YAML to JSON 踩坑笔记
后端·go·json
你驴我3 小时前
WhatsApp 消息撤回与编辑的幂等性设计实践
java·服务器·前端·后端·python
一缕清烟在人间3 小时前
HarmonyOS开发实战:小分享-TextEditPage文字编辑器——Header+TextArea+工具栏
后端·华为·harmonyos·鸿蒙
人间凡尔赛3 小时前
K8s 十年之变:从 Cloud Native 到 AI Native,2026 年后端架构的范式革命
后端·云原生·架构