【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
theme: smartblue
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
对于Httpclient请求机制进行设置操作处理。
@Body请求体模板
@Body注解申明一个请求体模板,模板中可以带有参数,与方法中@Param注解申明的参数相匹配,使用方法如下:
java
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/json")
// json curly braces must be escaped!
// 这里JSON格式需要的花括号居然需要转码,有点蛋疼了。
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void login(@Param("user_name") String user, @Param("password") String password);
}
...
client.login("denominator", "secret");
// {"user_name": "denominator", "password": "secret"}
Headers请求头
Feign支持给请求的api设置或者请求的客户端设置请求头,如下:
给API设置请求头
使用 @Headers 设置静态请求头
java
// 给BaseApi中的所有方法设置Accept请求头
@Headers("Accept: application/json")
interface BaseApi<V> {
// 单独给put方法设置Content-Type请求头
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String, V value);
}
设置动态值的请求头
java
@RequestLine("POST /")
@Headers("X-Ping: {token}")
void post(@Param("token") String token);
设置key和value都是动态的请求头
调用时动态确定使用不同的请求头
可以使用 @HeaderMap 注解,如下:
java
// @HeaderMap 注解设置的请求头优先于其他方式设置的
@RequestLine("POST /")
void post(@HeaderMap Map<String, Object> headerMap);
给Target设置请求头
有时我们需要在一个API实现中根据不同的endpoint来传入不同的Header,这个时候我们可以使用自定义的RequestInterceptor 或 Target来实现.
通过自定义的 RequestInterceptor 来实现请查看 Request Interceptors
下面是一个通过自定义Target来实现给每个Target设置安全校验信息Header的例子:
java
static class DynamicAuthTokenTarget<T> implements Target<T> {
public DynamicAuthTokenTarget(Class<T> clazz,
UrlAndTokenProvider provider,
ThreadLocal<String> requestIdProvider);
...
@Override
public Request apply(RequestTemplate input) {
TokenIdAndPublicURL urlAndToken = provider.get();
if (input.url().indexOf("http") != 0) {
input.insert(0, urlAndToken.publicURL);
}
input.header("X-Auth-Token", urlAndToken.tokenId);
input.header("X-Request-ID", requestIdProvider.get());
return input.request();
}
}
...
Bank bank = Feign.builder()
.target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));
-
这种方法的实现依赖于给Feign 客户端设置的自定义的RequestInterceptor 或 Target。可以被用来给一个客户端的所有api请求设置请求头。比如说可是被用来在header中设置身份校验信息。这些方法是在线程执行api请求的时候才会执行,所以是允许在运行时根据上下文来动态设置header的。
-
比如说可以根据线程本地存储(thread-local storage)来为不同的线程设置不同的请求头。
Base APIS
有些请求中的一些方法是通用的,但是可能会有不同的参数类型或者返回类型,这个时候可以这么用:
```java
// 通用API
interface BaseAPI {
@RequestLine("GET /health")
String health();
@RequestLine("GET /all")
List
```
Logging
你可以通过设置一个 Logger 来记录http消息,如下:
java
GitHub github = Feign.builder()
decoder(new GsonDecoder())
.logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
.logLevel(Logger.Level.FULL)
.target(GitHub.class, http://api.github.com);
Request Interceptors
当你希望修改所有的的请求的时候,你可以使用Request Interceptors。比如说,你作为一个中介,你可能需要为每个请求设置 X-Forwarded-For
java
static class ForwardedForInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
}
}
...
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new ForwardedForInterceptor())
.target(Bank.class, http://api.examplebank.com);
或者,你可能需要实现Basic Auth,这里有一个内置的基础校验拦截器
java
BasicAuthRequestInterceptor
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new BasicAuthRequestInterceptor(username, password))
.target(Bank.class, http://api.examplebank.com);
@Param Expansion
@Param 注解给模板中的参数设值的时候,默认的是使用的对象的 toString() 方法的值,通过声明 自定义的Param.Expander,用户可以控制其行为,比如说格式化 Date 类型的值:
java
// 通过设置 @Param 的 expander 为 DateToMillis.class 可以定义Date类型的值
@RequestLine("GET /?since={date}")
Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
Dynamic Query Parameters
动态查询参数支持,通过使用 @QueryMap 可以允许动态传入请求参数,如下:
java
@RequestLine("GET /find")
V find(@QueryMap Map<String, Object> queryMap);
自定义注解扫描动态生成客户端
原生Feign只能一次解析一个接口,生成对应的请求代理对象,如果一个包里有多个调用接口就要多次解析非常麻烦。
扩展BeanFactoryPostProcessor接口、
自定义注解:在扫描接口的过程中,可以通过一个自定义注解,来区分Feign接口并且指定调用的服务Url
实现扩展容器
java
@Component
public class FeignClientRegister implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
List<String> classes = scan(scanPath);
if(classes==null){
return ;
}
Feign.Builder builder = getFeignBuilder();
if(classes.size()>0){
for (String claz : classes) {
Class<?> targetClass = null;
try {
targetClass = Class.forName(claz);
String url=targetClass.getAnnotation(FeignApi.class).serviceUrl();
if(url.indexOf("http://")!=0){
url="http://"+url;
}
Object target = builder.target(targetClass, url);
beanFactory.registerSingleton(targetClass.getName(), target);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
public Feign.Builder getFeignBuilder(){
Feign.Builder builder = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.options(new Request.Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3));
return builder;
}
public List<String> scan(String path){
ScanResult result = new FastClasspathScanner(path).matchClassesWithAnnotation(FeignApi.class, (Class<?> aClass) -> {
}).scan();
if(result!=null){
return result.getNamesOfAllInterfaceClasses();
}
return null;
}
}
- 完整秒杀架构的设计到技术关键点的“情报信息”
- 独一无二的「MySQL调优金字塔」相信也许你拥有了它,你就很可能拥有了全世界。
- 【MySQL技术之旅】(5)该换换你的数据库版本了,让我们一同迎接8.0的到来哦!(初探篇)
- ☕【Java技术指南】「Java8编程专题」让你真正会用对Java新版日期时间API编程指南
- 【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
- 【优化技术专题】「线程间的高性能消息框架」终极关注Disruptor的核心源码和Java8的@Contended伪共享指南
- 【优化技术专题】「线程间的高性能消息框架」再次细节领略Disruptor的底层原理和优势分析
- 【Zookeeper核心原理】Paxos协议的原理和实际运行中的应用流程分析
- ☕【Java技术指南】「JPA编程专题」让你不再对JPA技术中的“持久化型注解”感到陌生了!
- Java技术开发专题系列之【Guava RateLimiter】针对于限流器的入门到精通(含源码分析介绍)
- ☕【Java技术指南】「JPA编程专题」让你不再对JPA技术中的“持久化型注解”感到陌生了!
- 【Eureka技术指南】「SpringCloud」从源码层面让你认识Eureka工作流程和运作机制(下)
- MySQL技术专题(6)这也许是你的知识盲区-MySQL主从架构以及[半同步机制]
- 优化技术专题-线程间的高性能消息框架-深入浅出Disruptor的使用和原理
- ☕【Java技术指南】「并发编程专题」Fork/Join框架基本使用和原理探究(原理篇)
- ☕【Java技术指南】「并发编程专题」Guava RateLimiter针对于限流器的入门到精通(含源码分析介绍)
- 【优化技术专题】「温故而知新」基于Quartz系列的任务调度框架的动态化任务实现分析
- ☕【Java技术指南】「并发编程专题」Guava RateLimiter针对于限流器的入门到精通(含实战和原理分析)
- 【MySQL技术之旅】(4)这也许是你的知识盲区-[MySQL主从架构]之半同步机制
- ☕【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)