【杂谈Spring】Spring服务调用组件RestTemplate

Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如 Android或者第三方服务商都是使用 RestTemplate 请求 restful 服务。

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。接下来我们就来看看这些操作方法的使用。

RestTemplate是spring-web依赖下的。

RestTemplate包含以下几个部分: 

  • HttpMessageConverter 对象转换器
  • ClientHttpRequestFactory 默认是JDK的HttpURLConnection
  • ResponseErrorHandler 异常处理
  • ClientHttpRequestInterceptor 请求拦截器

构造RestTemplate的Bean实例

构造一个RestTemplate的Bean实例很容易,只需这样配置

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

但我们希望更多得了解一个这个构造过程做了哪些事情。为此,我们需要去打开RestTemplate这个黑盒子。

RestTemplate设计

先试着从RestTemplate得类图上获得一些灵感:

我们看到,简单的几个类和接口展示了标准的抽象设计。

HttpAccessor是一个抽象类,它抽象的是一个http访问器的概念。这说明,RestTemplate具备http方式请求响应的处理能力。再看RestOperations,它是一个接口,抽象的是restful风格的操作方法,这意味着RestTemplate的操作风格将是Restful的。

http访问器和restful风格操作做了一个分离。RestTemplate则是聚合了两者,既有http访问器的基础能力,又在这个基础之上构建了restful风格。设计者把Restful操作作为接口,其实从规范角度上,我们应该面向RestOperations接口使用它。但大多数人似乎都直接使用RestTemplate。

RestTemplate实例构造过程

接下来,我们再看看new一个RestTemplate对象做了哪些事情。

进入RestTemplate类的构造方法

private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();

private UriTemplateHandler uriTemplateHandler;

public RestTemplate() {
    // 添加HttpMessageConverterjie
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(new StringHttpMessageConverter());
    this.messageConverters.add(new ResourceHttpMessageConverter(false));
    try {
        this.messageConverters.add(new SourceHttpMessageConverter<>());
    }
    catch (Error err) {
        // Ignore when no TransformerFactory implementation is available
    }
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

    if (romePresent) {
        this.messageConverters.add(new AtomFeedHttpMessageConverter());
        this.messageConverters.add(new RssChannelHttpMessageConverter());
    }

    if (jackson2XmlPresent) {
        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
    }
    else if (jaxb2Present) {
        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }

    if (jackson2Present) {
        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    else if (gsonPresent) {
        this.messageConverters.add(new GsonHttpMessageConverter());
    }
    else if (jsonbPresent) {
        this.messageConverters.add(new JsonbHttpMessageConverter());
    }

    if (jackson2SmilePresent) {
        this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
    }
    if (jackson2CborPresent) {
        this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
    }
    // uri模板处理器
    this.uriTemplateHandler = initUriTemplateHandler();
}

构造方法有点长,但只做了两件事:

  • 添加HttpMessageConverter的实现类,熟悉springmvc的话估计知道HttpMessageConverter。顾名思义,它就是用来转换http请求响应过程中的消息数据的。
  • 初始化一个UriTemplateHandler

我们跟进initUriTemplateHandler方法,看看怎么初始化一个uri模板处理器的

private static DefaultUriBuilderFactory initUriTemplateHandler() {
    DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
    uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT);  // for backwards compatibility..
    return uriFactory;
}

这里只是简单地构造了实例,并直接返回。由此可见,DefaultUriBuilderFactory应该直接或者间接实现了UriTemplateHandler

我们看看DefaultUriBuilderFactory的类图

与我们猜想的一样,DefaultUriBuilderFactory间接实现了UriTemplateHandler

RestTemplate的构造方法中就做了这两件事,我们再看看HttpAccessor

public abstract class HttpAccessor {
    // ...

    private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

    // ...
}

默认创建了一个ClientHttpRequestFactory的实例,ClientHttpRequestFactory这个接口是用来创建客户端的request请求对象的,我们并没有去自定义设置它默认采用SimpleClientHttpRequestFactory这个实现类。

ClientHttpRequestFactory作为工厂模式,将会生产ClientHttpRequest。ClientHttpRequest抽象了Http请求操作,执行ClientHttpRequest将会发送请求与服务端交互。

我们再大体过一下RestTemplate的构造过程:

(1) 在RestTemplate构造方法中添加了多个HttpMessageConverter后续用于http请求响应的数据转换

(2) 在RestTemplate构造方法中初始化了一个Uri模板的处理器,后续用于处理uri相关的东西

(3) HttpAccessor默认创建了一个ClientHttpRequestFactory的成员实例,后续用于创建请求对象。

快速入门

1、创建两个web项目:服务调用方web-consumer和服务提供方web-producer。

2、服务提供方web-producer,创建UserController,提供了如下http接口:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
@RequestMapping("/userInfo")
public class UserController {

    private static Map<Long, User> userMap = new HashMap();

    static {
        userMap.put(1L, new User(1L, "张三", 21, "广东省"));
        userMap.put(2L, new User(2L, "栗子", 23, "广西省"));
        userMap.put(3L, new User(3L, "李四", 25, "山东省"));
        userMap.put(4L, new User(4L, "孙斌", 23, "海南省"));
        userMap.put(5L, new User(5L, "刘兰", 25, "宁夏省"));
    }

    @GetMapping("getById")
    public User getById(Long id) {
        return userMap.get(id);
    }

    @PostMapping("listAll")
    public List<User> listAll() {
        return new ArrayList(userMap.values());
    }

    @GetMapping("selectList")
    public List<User> selectList(@RequestParam("ids[]") List<Long> ids) {
        List<User> list = new ArrayList();
        for (Long id : ids) {
            list.add(userMap.get(id));
        }
        return list;
    }

    @PostMapping("queryByCondition")
    public List<User> queryByCondition(@RequestBody Map<String, Object> params) {
        String name = (String) params.get("name");
        Integer age = (Integer) params.get("age");
        Set<Map.Entry<Long, User>> entries = userMap.entrySet();
        Iterator<Map.Entry<Long, User>> iterator = entries.iterator();
        List<User> resultList = new ArrayList();
        while (iterator.hasNext()) {
            Map.Entry<Long, User> entry = iterator.next();
            if (entry.getValue().getName().contains(name) || entry.getValue().getAge() >= age) {
                resultList.add(entry.getValue());
            }
        }
        return resultList;
    }

}

class User implements Serializable {
    private Long id;
    private String name;
    private Integer age;
    private String address;

    public User() {

    }

    public User(Long id, String name, Integer age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

访问地址为:http://localhost:8080/userInfo/xxxx

3、服务调用方web-consumer,创建RestTemplate实例

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

4、服务调用方web-consumer通过RestTemplate实例调用服务提供方web-producer暴露的http服务

@RestController
public class HelloController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/query")
    public Object query(){
        Map<String, Object> resultMap = restTemplate.getForObject("http://localhost:8080/userInfo/getById?id=1", Map.class);
        return resultMap;
    }
}

请求结果如下:

RestTemplate 相关API

 

posted @ 2021-12-02 10:39  残城碎梦  阅读(317)  评论(0编辑  收藏  举报