JAVA 项目中接口调用怎么做?
1 Httpclient
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。
HttpClient 相比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。
2. Okhttp
一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代 HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持多种协议(HTTP/2 和 SPDY)。
3. HttpURLConnection
HttpURLConnection 是
Java
的标准类,它继承自 URLConnection,可用于向指定网站发送 GET 请求、POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。
4.RestTemplate
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。
5. Feign
Feign 是一个声明式的 REST 客户端,它能让 REST 调用更加简单。Feign 供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。
而 Feign 则会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。
Spring Cloud
对 Feign 进行了封装,使其支持 SpringMVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
在Spring Boot 中集成Feign
1 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.0</version>
</dependency>
2 通过@EnableFeignClients开启 feign 功能
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3 编写 feign接口进行远程调用
//${}可以应用application.yml内的配置,path会拼接到最后面
@FeignClient(value = "feignInterface",url="${test.url}/test",path = "/service",configuration = GlobalFeignConfig.class)
public interface FeignInterface {
//请求 ${test.url}/test/service/hello 接口
@GetMapping("/hello")
public String getNameByParam( @RequestParam String userName );
}
Feign配置类
@FeignClient内的configuration可以对 feign请求进行先关配置
public class GlobalFeignConfig implements RequestInterceptor {
@Bean
public OkHttpInterceptor okHttpInterceptor() {
return new OkHttpInterceptor();
}
@Bean
public okhttp3.OkHttpClient okHttpClient() {
return new okhttp3.OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool())
.addInterceptor(okHttpInterceptor())
.build();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public ErrorDecoder errorDecoder() {
return new ErrorDecoder.Default();
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new PhpMappingJackson2HttpMessageConverter());
return () -> httpMessageConverters;
}
class PhpMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
PhpMappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8")); //关键
setSupportedMediaTypes(mediaTypes);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
setObjectMapper(objectMapper);
}
}
/**
* 如果需要basic认证
*/
// @Bean
// public BasicAuthenticationInterceptor basicAuthenticationInterceptor(){
// return new BasicAuthenticationInterceptor();
// }
}
OkHttpInterceptor: 拦截器可以设置请求头等信息
@Slf4j
public class OkHttpInterceptor implements HandlerInterceptor, Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
log.debug("进入okhttp拦截器");
Request request = chain.request();
Request.Builder builder = request.newBuilder();
//加上租户标识
if(StrUtil.isNotBlank(SessionValueHolder.getCompanyCode())){
builder.header(Constants.COMPANY_CODE, SessionValueHolder.getCompanyCode());
}
//加上token
if(StrUtil.isNotBlank(SessionValueHolder.getAuthorization())){
builder.header(Constants.AUTHORIZATION_KEY, SessionValueHolder.getAuthorization());
}
//加上feign调用标识
builder.header(Constants.FEGIN_FLAG, "1");
return chain.proceed(builder.build());
}
}
feign 配置文件相关配置
# 链接超时时间 feignName写自己feign接口的类名
feign.client.config.feignName.connectTimeout=5000
# 读取超时时间
feign.client.config.feignName.readTimeout=5000
# 日志等级
feign.client.config.feignName.loggerLevel=full
# 重试
feign.client.config.feignName.retryer=com.example.SimpleRetryer
# 拦截器
feign.client.config.feignName.requestInterceptors[0]=com.example.FooRequestInterceptor
feign.client.config.feignName.requestInterceptors[1]=com.example.BarRequestInterceptor
# 编码器
feign.client.config.feignName.encoder=com.example.SimpleEncoder
# 解码器
feign.client.config.feignName.decoder=com.example.SimpleDecoder
# 契约
feign.client.config.feignName.contract=com.example.SimpleContract
#开启GZIP压缩,请求数据量很大的接口提高性能
#对请求数据进行压缩
feign.compression.request.enabled=true
#对响应数据进行压缩
feign.compression.response.enabled=true
#指定 请求方式 进行压缩
feign.compression.request.mime-types=text/xml,application/xml,application/json
#设置最小压缩大小,只有超过了这个大小才会进行压缩
feign.compression.request.min-request-size=2048
实战练习
通过Feign调用我们实现申明的接口进行测试:
Controller
@RestController
@RequestMapping("service")
public class ServiceController {
//请求参数
@GetMapping("/param")
public String getNameByParam( String userName ){
return userName;
}
//请求体
@PostMapping("/body")
public String getNameByBody( @RequestBody String userName ){
return userName;
}
@GetMapping("/timeout/5000")
public String timeout( @RequestParam(required = false,defaultValue = "5000") long millis ) throws Exception {
Thread.sleep( millis );
return "success";
}
//单文件
@PostMapping(value = "/file",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String file(@RequestPart("file2") MultipartFile multipartFile){
if( multipartFile == null ) return null;
return multipartFile.getName();
}
//多文件
@PostMapping("/files")
public Iterable<String> files(MultipartRequest multipartRequest){
if( multipartRequest == null ) return null;
List<String> fileNameArray = new ArrayList();
if( multipartRequest instanceof StandardMultipartHttpServletRequest){
StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) multipartRequest;
List<String> fileNameList = request.getMultiFileMap().values().stream()
.flatMap(o -> o.stream())
.map( MultipartFile::getOriginalFilename ).collect(Collectors.toList());
return fileNameList;
}
Iterator<String> fileNames = multipartRequest.getFileNames();
while(fileNames.hasNext()){
String fileName = fileNames.next();
fileNameArray.add( multipartRequest.getFile(fileName).getOriginalFilename() );
}
return fileNameArray;
}
}
Get请求传递参数
@GetMapping("/param")
public String getNameByParam( @RequestParam String userName );
Post请求传递请求头
@PostMapping("/body")
public String getNameByBody( @RequestBody String userName );
路径参数
@GetMapping("/timeout/{millis}")
public String timeout( @PathVariable long millis );
文件上传
//必须设置MediaType.MULTIPART_FORM_DATA_VALUE 使用 @RequestPart接收form格式的文件数据
@PostMapping(value="/file",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String file(@RequestPart("file2") MultipartFile multipartFile);
多文件上传
@PostMapping(value="/files",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Iterable<String> files( @RequestPart("file2") Collection<MultipartFile> file);