前言
SpringCloud微服务之间的请求一般使用OpenFeign
,有时候我们需要在请求或者响应的时候做一些额外的操作。比如请求的时候添加请求头,响应的时候判断token是否过期等等。这时候拦截器就派上用场了!
我们接下来就说一下怎么添加请求和响应拦截器。
一、修改OpenFeign的http客户端
OpenFeign默认的http客户端是javax.net.ssl.HttpsURLConnection
,详细信息见feign-core:feign.Client
,该http客户端不支持添加拦截器和连接池。所以我们需要添加第三方http客户端,可选的http客户端有httpClient
和okHttp
,对应的依赖如下:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.7.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>9.7.0</version>
</dependency>
OpenFeign默认启用的是HttpClient
,但是我使用的是OkHttp
,故添加以下配置
feign.httpclient.enabled=false
feign.okhttp.enabled=true
添加以上配置后,OpenFeign的http客户端就自动切换为OkHttp了,详细过程看源码就清楚了,org.springframework.cloud.openfeign.FeignAutoConfiguration
,org.springframework.cloud.openfeign.ribbon.OkHttpFeignLoadBalancedConfiguration
。在此就不赘述了。
二、添加请求拦截器
OpenFeign官方自带请求拦截器的接口feign.RequestInterceptor
,我们只要实现该接口就可以了。然后你就可以做你想做的任何事情了
@Component
public class CustomerFeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
template.header("sessionId", request.getHeader("sessionId"));
}
}
}
三、添加响应拦截器
刚开始我以为OpenFeign官方有响应拦截器的接口,但是我发翻了半天源码也没找到,所以就想在OkHttp上打主意。
网上很多文章里的办法都是重新构造OkHttpClient
的Bean,
但是通过查看源码org.springframework.cloud.openfeign.FeignAutoConfiguration
发现有这个配置:@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
,大概意思就是如果没有OkHttpClient
这个Bean,就启动这个配置,如果存在OkHttpCLient
的Bean,则不启用这个配置。
如果我们自定义OkHttpClient
的Bean,将导致该配置不能生效,我们可能就需要添加很多配置,这简直就是费力不讨好嘛,那有没有更好的方法呢??肯定是有的!!
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
我们仔细观察OkHttpFeignConfiguration
这个内部类里是怎么配置OkHttpClient
的,发现是通过Builer建造者模式创建的对象,OkHttpClient.Builder
又是通过OkHttpClientFactory
接口来生成的,OkHttpClientFactory
接口的默认实现DefaultOkHttpClientFactory
的构造参数又是OkHttpClient.Builder
。
还有很重要的一点,OkHttpClientFactory
在FeignAutoConfiguration
整个类中并没有实例化,只有在方法参数中引用了,这说明OkHttpClientFactory
在其他地方已经实例化了,已经是spring管理的Bean了。接下来我们要找到OkHttpClientFactory
是在哪里实例化的。在Idea中打开OkHttpClientFactory
这个接口的代码,按住Ctrl加鼠标左键,出现以下内容
一共有7个地方引用了OkHttpClientFactory
,但是只有HttpClientConfiguration
是我们没有接触过的,其他的上面都有涉及。所以OkHttpClientFactory
很有可能就是在这个类里实例化的,打开这个类,果然如此!!
前面说到,OkHttpClient
的实例化和OkHttpClient.Builder
、OkHttpClientFactory
有关,OkHttpClientFactory
的实例化又和OkHttpClient.Builder
有关。
所以,我们只要自定义OkHttpClient.Builder
就能完美解决这个问题!!自定义OkHttpClient.Builder
的时候添加响应拦截器就可以了。切记重新构造Response
返回,如果直接返回Response
,会导致Feign直接走降级。
@Slf4j
@Configuration
public class FeignOkHttpClientConfig {
@Bean
public OkHttpClient.Builder okHttpClientBuilder() {
return new OkHttpClient.Builder().addInterceptor(new FeignOkHttpClientResponseInterceptor());
}
/**
* okHttp响应拦截器
*/
public static class FeignOkHttpClientResponseInterceptor implements Interceptor{
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Response response = chain.proceed(originalRequest);
MediaType mediaType = response.body().contentType();
String content = response.body().string();
//解析content,做你想做的事情!!
//生成新的response返回,网络请求的response如果取出之后,直接返回将会抛出异常
return response.newBuilder()
.body(ResponseBody.create(mediaType, content))
.build();
}
}
}
写在最后的话
刚开始我也是直接复制网上的方法,发现不太好用,然后花了一点时间看了下源码,找到了解决方法。所以有时候直接百度也不太好用,还是得靠自己。
来源:blog.csdn.net/WayneLee0809/article/ details/111474951 精彩推荐 最全的java面试题库 开源版的高仿 “ 微信 ”,吊炸天! 与其在网上拼命找题? 不如马上关注我们~
↓点阅读原文,Java面试题库尽情看!
原文始发于微信公众号(Java面试题精选):SpringCloud OpenFeign 使用OkHttp,添加响应拦截器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/43757.html