1. Spring MVC基本介绍
2. Spring MVC基本操作配置及其运行流程
3. Spring MVC视图解析器
4. Spring MVC前后端数据互交(Controller、RequestMapping、RequestBody、RequestParam、ModelAndView等)
5. Spring ResponseEntity对象详细及其源码分析
6. Spring MVC RESTful风格
7. HTTP请求方法与HTTP请求状态码
8. Spring服务器端数据校验(JSR-303)
9. Spring文件上传与下载(MultipartFile对象)
10. HttpClient、RestTemplate、OkHttp特点? RestTemplate请求模板详细使用
11. Spring MVC拦截器使用
12. Spring MVC统一异常处理
13. Spring MVC的九大组件概述
14. Spring MVC地区信息(LocaleResolver)
15. Spring MVC主题(ThemeSource)
远程调用方式 | 描述 |
---|---|
HttpClient | 代码复杂,还得操心资源回收等。代码很复杂,冗余代码多,不建议直接使用 |
RestTemplate | Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率 |
OkHttp | OkHttp是一个高效的HTTP客户端 |
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等
HTTPClient可以作为SOA架构的服务处理组件,在一些简单的服务请求上HttpClien它的小巧轻便可以解决服务之间请求的问题,相对于Dobbo框架来说HttpClient可以轻便的实现服务器之间的相互调用;但是在大型的项目中HttpClient就显得不如Dubbo框架
从Spring 3.0开始支持的一个HTTP请求工具,提供了常见的REST请求方案的模版。提供了用于访问Http请求的客户端,RestTemple提供了多种简洁的远程访问服务的方法,省去了很多无用的代码(提供了六种常用的HTTP方法实现远程服务调用)
(1)OkHttp是一个高效的HTTP客户端,允许所有同一个主机地址的请求共享同一个socket连接;连接池减少请求延时;透明的GZIP压缩减少响应数据的大小;缓存响应内容,避免一些完全重复的请求
(2)当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试配置的其他IP,OkHttp使用现代TLS技术(SNI,ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0
从Spring 3.0开始支持的一个HTTP请求工具,提供了常见的REST请求方案的模版。提供了用于访问Http请求的客户端,RestTemple提供了多种简洁的远程访问服务的方法,省去了很多无用的代码(提供了六种常用的HTTP方法实现远程服务调用)
Spring框架提供的RestTemplate类可用于在应用中调用REST服务,简化了与http服务的通信方式,统一了REST风格的标准,封装了http链接,只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式
在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring模板类(JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法
RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory()替换为例如Apache HttpComponents、Netty或OkHttp等其它HTTP library
RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。
例如:RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法;GET、POST、PUT、DELETE以及一些通用的请求执行方法exchange以及execute。
RestTemplate继承自InterceptingHttpAccessor并且实现了RestOperations接口。RestOperations接口定义了基本的REST风格操作,这些操作在RestTemplate中都得到了实现
RestTemplate有3个构造方法,一个无参构造,两个有参构造
使用默认配置创建一个RestTemplate实例,默认的HttpMessageConverter集合被初始化
使用指定的ClientHttpRequestFactory创建一个RestTemplate实例。
方法 | 描述 |
---|---|
requestFactory | 用于创建HTTP请求的工厂,默认的实现有SimpleClientHttpRequestFactory、HttpComponentsClientHttpRequestFactory |
如果没有设置默认是SimpleClientHttpRequestFactory
传入自定义的HttpMessageConverter集合,并赋值给messageConverters,之后使用自定义的HttpMessageConverter
ClientHttpRequestFactory
Spring定义的一个接口,用于生产org.springframework.http.client.ClientHttpRequest对象,RestTemplate只是模板类,抽象了很多调用方法,而底层真正使用何种框架发送HTTP请求是通过ClientHttpRequestFactory指定的。
RestTemplate可以在构造时设置ClientHttpRequestFactory,也可以通过setRequestFactory()设置。
Spring的两种ClientHttpRequestFactory的实现类
SimpleClientHttpRequestFactory和HttpComponentsClientHttpRequestFactory
(1)SimpleClientHttpRequestFactory
如果什么都不设置,RestTemplate默认使用的是SimpleClientHttpRequestFactory,其内部使用的是JDK的java.net.HttpURLConnection创建底层连接,默认是没有连接池的,connectTimeout和readTimeout都是 -1,即没有超时时间
(2)HttpComponentsClientHttpRequestFactory
HttpComponentsClientHttpRequestFactory底层使用Apache HttpClient创建请求,访问远程的Http服务,可以使用一个已经配置好的HttpClient实例创建HttpComponentsClientHttpRequestFactory请求工厂,HttpClient实例中可以配置连接池和证书等信息
在RestTemplate中定义了11个独立的操作
RestTemplate的方法名遵循一定的命名规范:第一部分表示用哪种HTTP方法调用(get、post),第二部分表示返回类型
方法 | 描述 |
---|---|
getForObject() | 发送GET请求,将HTTP response转换成一个指定的Object对象 |
postForEntity() | 发送POST请求,将给定的对象封装到HTTP请求体,返回类型是一个HttpEntity对象 |
每个HTTP方法对应的RestTemplate方法都有3种。其中2种的url参数为字符串,URI参数变量分别是Object数组和Map,另外一种url使用URI类型作为参数。
getForObject(String url, Class responseType, Object... uriVariables)
getForObject(String url, Class responseType, Map uriVariables)
getForObject(URI url, Class responseType)
注意:使用字符串类型的url,默认会对url进行转义,如http://example.com/hotel list在执行时会转义http://example.com/hotel%20list,这样其实是没有问题的,但如果字符串类型的url本身已经转义过了,执行时就会再转义一次,变成http://example.com/hotel%2520list。如果不需要这种隐式的转义,可以使用java.net.URI参数的方法,这种方法不会在执行时存在隐式的url转义,可以在创建URI对象时自行决定是否转义,推荐使用UriComponentsBuilder创建
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://example.com/hotels/{hotel}/bookings/{booking}")
.build()
.expand("42", "21")
.encode();
URI uri = uriComponents.toUri();
//build(true)就不会对url转义,但如果包含http://example.com/hotel list这种需要转义的url,会报错
exchange()和execute()比其它方法(如getForObject、postForEntity等)使用范围更广,允许调用者指定HTTP请求的方法(GET、POST、PUT等),并且可以支持像HTTP PATCH(部分更新),但需要底层的HTTP库支持,JDK自带的HttpURLConnection不支持PATCH方法,Apache的HTTPClient 4.2及以后版本支持。
方法 | 描述 |
---|---|
getForEntity() | 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象。比如:响应体数据为一个由User转化的json,那么它被封装进ResponseEntity时将转回User对象。用一个ResponseEntity对象接收该方法返回值后,可取出其中的响应体对象、响应头和响应状态 |
getForObject() | 发送一个HTTP GET请求,返回根据响应体映射形成的对象。同上,不过只返回User对象 |
postForEntity() | POST数据到一个URL,返回的ResponseEntity包含了响应体所映射成的对象 |
postForObject() | POST数据到一个URL,返回根据响应体映射形成的对象 |
postForLocation() | POST数据到一个URL,返回新创建资源的URL |
put() | PUT资源到特定的URL |
delete() | 在特定的URL上对资源执行HTTP DELETE操作 |
execute() | 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象 |
exchange() | 在URL上执行特定的HTTP方法,返回的ResponseEntity包含了响应体所映射成的对象,这个对象是从响应体中映射得到的 |
headForHeaders() | 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头 |
optionsForAllow() | 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息 |
RestTemplate发送的是HTTP请求,那么在响应的数据中必然也有响应头,如果开发者需要获取响应头的话,那么就需要使用getForEntity来发送HTTP请求,此时返回的对象是一个ResponseEntity的实例。这个实例中包含了响应数据以及响应头
RestTemplate参数 | 描述 |
---|---|
url | String类型或URI类型的请求地址。请求的URL。表示要调用的服务的地址 |
responseType | 指定返回的实体类型(body),Class对象。负责承载数据的类 |
uriVariables | URI参数,可以是变长数组或map。类似预编译,为防止URL写死,为URL的参数赋值 |
返回值:responseType指定的Entity类型或Object类型。ResponseEntity是Spring对HTTP响应的封装,包括了几个重要的元素。如响应码、contentType、contentLength、response header信息、response body信息等。返回的消息体的数据类型
返回值一般为第二参数Class
getForEntity()的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素:响应码、contentType、contentLength、响应消息体等。
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/gethello")
public String getHello() {
ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8080/hello", String.class);
String body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();
StringBuffer result = new StringBuffer();
result.append("responseEntity.getBody()")
.append(body)
.append("
")
.append("responseEntity.getStatusCode():")
.append(statusCode).append("
").append("responseEntity.getStatusCodeValue():")
.append(statusCodeValue).append("
").append("responseEntity.getHeaders():").append(headers)
.append("
");
return result.toString();
}
有时候在调用服务提供者提供的接口时,可能需要传递参数,有两种不同的方式,如下:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/sayhello")
public String sayHello() {
ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8080/sayhello?name={1}",String.class, "张三");
return responseEntity.getBody();
}
@RequestMapping("/sayhello2")
public String sayHello2() {
Map map = new HashMap<>();
map.put("name", "李四");
ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8080/sayhello?name={name}",
String.class, map);
return responseEntity.getBody();
}
可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符,也可以前面使用name={name}这种形式,最后一个参数是一个map,map的key即为前边占位符的名字,map的value为参数值
第一个调用地址也可以是一个URI而不是字符串,这个时候我们构建一个URI即可,参数神马的都包含在URI中了,如下:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/sayhello3")
public String sayHello3() {
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}")
.build()
.expand("王五")
.encode();
URI uri = uriComponents.toUri();
ResponseEntity responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
通过Spring中提供的UriComponents来构建Uri即可。
当然,服务提供者不仅可以返回String,也可以返回一个自定义类型的对象,比如我的服务提供者中有如下方法:
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/getbook1", method = RequestMethod.GET)
public Book book1() {
return new Book("三国演义", 90, "罗贯中", "花城出版社");
}
对于该方法我可以在服务消费者中通过如下方式来调用:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/book1")
public Book book1() {
ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8080/getbook1", Book.class);
return responseEntity.getBody();
}
getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/book2")
public Book book2() {
Book book = restTemplate.getForObject("http://localhost:8080/getbook1", Book.class);
return book;
}
在RestTemplate中,POST请求可以通过如下三个方法来发起:
该方法和get请求中的getForEntity方法类似,如下例子:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/book3")
public Book book3() {
Book book = new Book();
book.setName("红楼梦");
ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:8080/getbook2", book, Book.class);
return responseEntity.getBody();
}
这里创建了一个Book对象,这个Book对象只有name属性有值,将之传递到服务提供者那里去,服务提供者代码如下:
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/getbook2", method = RequestMethod.POST)
public Book book2(@RequestBody Book book) {
book.setPrice(33);
book.setAuthor("曹雪芹");
book.setPublisher("人民文学出版社");
return book;
}
服务提供者接收到服务消费者传来的参数book,给其他属性设置上值再返回,调用结果如下:
如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。
postForLocation也是提交新资源,提交成功之后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只需要服务提供者返回一个Uri即可,该Uri表示新资源的位置
在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已。举一个简单的例子,如下:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/put")
public void put() {
Book book = new Book();
book.setName("红楼梦");
restTemplate.put("http://HELLO-SERVICE/getbook3/{1}", book, 99);
}
Book对象是要提交的参数,最后的99用来替换前面的占位符{1}
delete请求我们可以通过delete方法调用来实现,如下例子:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/delete")
public void delete() {
restTemplate.delete("http://HELLO-SERVICE/getbook4/{1}", 100);
}
delete方法也有几个重载的方法,不过重载的参数和前面基本一致
(1)可以支持多种HTTP方法,在参数中指定。提供统一的方法模板进行四种请求:POST、PUT、DELETE、GET
(2)可以在请求中增加header和body信息,返回类型是ResponseEntity,可以从中获取响应的状态码,header和body等信息
exchange(String url,
HttpMethod method,
@Nullable HttpEntity> requestEntity,
ParameterizedTypeReference responseType,
Map uriVariables);
参数 | 描述 |
---|---|
url | 请求地址 |
method | 请求类型(如:POST、PUT、DELETE、GET) |
requestEntity | 请求实体。封装请求头,请求内容 |
responseType | 响应类型。根据服务接口的返回类型决定 |
uriVariables | url中参数变量值 |
@Autowired
private RestTemplate restTemplate;
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity requestEntity = new HttpEntity(requestHeaders);
HttpEntity response = restTemplate.exchange(
"http://example.com/hotels/{hotel}",
HttpMethod.GET, // GET请求
requestEntity, // requestEntity,可以设置请求header、body
String.class, "42");
// 响应头信息
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();