openfeign 使用方法和执行流程
1.用法
1.1引入依赖
<!-- feign client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
1.3参数校验(利用MethodValidationInterceptor 再springContext中利用@Validated生成代理对象来进行参数校验)
@Validated
@FeignClient(name="nuts", url = "${nuts.sms.smsNoticeUrl}",configuration = {HttpClientProxyConfiguration.class})
public interface NoticeSao { @PostMapping("/tesyt")
@Async
ResponseDTO sendSms(@Valid NoticeDTO noticeDTO, URI uri);
}
1.2 url配置的优化级 从高到低依次覆盖
@FeignClient(name="nuts", url = "${nuts.sms.smsNoticeUrl}",configuration = {HttpClientProxyConfiguration.class})
public interface NoticeSao {
@PostMapping("/tesyt")
@Async
ResponseDTO sendSms(NoticeDTO noticeDTO, URI uri);
}
public void apply(RequestTemplate template) {
// template.target("http://test/test");
}
1.2.1 在参数中加上URI
1.3.2 @FeignClient 中的url带有http参数
1.4.3 在拦截器中使用 template.target("http://test/test");
1.3配置拦截器 (注意拦截器使用范围)
@Component
@Slf4j
public class NutsOpenApiInterceptor implements RequestInterceptor {
1.4配置 HttpClientProxyConfiguration
@FeignClient(name="nuts",3.url = "",configuration = {HttpClientProxyConfiguration.class})
public interface NoticeSao {
1.5遗留问题
1.5.1 再集群中服务发现 和url 手动指定的矛盾化解
2.原理
2.1 启用配置类
2.1.1 FeignAutoConfiguration
2.1.2 @EnableFeignClients (FeignClientsRegistrar)
FeignClientsRegistrar 的作用:
1.注册默认的configuration,
2.注册FeignClients即有@FeignClient注解的接口
3.注册@FeignClient(name="nuts",3.url = "",configuration = {HttpClientProxyConfiguration.class})里的configuration到当前的content
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
2.2 初始FeignContext
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
为@FeignClient注解的类创建springContext,parentContext均为当前的springContxt;
2.2 创建@FeignClient注解的接口的bean对象
1.创建一个 FeignClientFactoryBean 在初始话时,注入各种需要的对象。
2.需要注入接口的地方,会调用 FeignClientFactoryBean.getBean方法
3.getBean中初始话builder Feign.Builder
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
}
Retryer retryer = getOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
if (errorDecoder != null) {
builder.errorDecoder(errorDecoder);
}
Request.Options options = getOptional(context, Request.Options.class);
if (options != null) {
builder.options(options);
}
Map<String, RequestInterceptor> requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
} if (this.decode404) {
builder.decode404();
}
}
4.使用动态代理生成代理类 ReflectiveFeign
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
5. 调用请求具体的方法 SynchronousMethodHandler
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
问题:
目前feign不支持 异步调用接收返回值
下面写法是错误的,目前feign不支持这样写。
@PostMapping("/")
@Async
Future<ResponseDTO> sendSms(NoticeDTO noticeDTO);
采用另一种解决方案,在service中做异步
@Async
public Future<ResponseDTO> asyncSendSms(NoticeDTO noticeDTO){
ResponseDTO responseDTO = noticeSao.sendSms(noticeDTO);
return new AsyncResult(responseDTO);
}
用法:
public class BspEncoder implements Encoder {
private static final String CONTENT_TYPE_HEADER;
private static final Pattern CHARSET_PATTERN;
static {
CONTENT_TYPE_HEADER = "Content-Type";
CHARSET_PATTERN = Pattern.compile("(?<=charset=)([\\w\\-]+)");
}
@Value("${accessCode}")
private String accessCode;
@Value("${checkword}")
private String checkword;
private ContentProcessor processor=new UrlencodedFormContentProcessor();
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
//1.将object生成xml String
com.sf.wms.sao.encoder.annotation.Request annotation = AnnotationUtils.findAnnotation((Class<?>) bodyType, com.sf.wms.sao.encoder.annotation.Request.class);
Request request=new Request();
request.setService(annotation.service());
request.setLang(annotation.lang());
request.setHead(accessCode);
String xml="";
try {
xml= JAXBUtil.writeToString(request, (Class) bodyType, request.getClass());
} catch (Exception e) {
e.printStackTrace();
}
//instanceof 判断某个对象是否是某一类型
// obj.getClass().isArray();
//2.生成 verifyCode
String verifyCode = md5AndBase64(xml + checkword);
Map<String,Object> map=new HashMap<>();
map.put("xml",xml);
map.put("verifyCode",verifyCode);
String contentTypeValue = getContentTypeValue(template.headers());
val charset = getCharset(contentTypeValue);
processor.process(template,charset,map);
}
private String getContentTypeValue (Map<String, Collection<String>> headers) {
for (val entry : headers.entrySet()) {
if (!entry.getKey().equalsIgnoreCase(CONTENT_TYPE_HEADER)) {
continue;
}
for (val contentTypeValue : entry.getValue()) {
if (contentTypeValue == null) {
continue;
}
return contentTypeValue;
}
}
return null;
}
private Charset getCharset (String contentTypeValue) {
val matcher = CHARSET_PATTERN.matcher(contentTypeValue);
return matcher.find()
? Charset.forName(matcher.group(1))
: UTF_8;
}
private static byte[] md5(String data) {
return DigestUtils.md5(data);
}
private static String md5AndBase64(String data) {
return base64Encode(md5(data));
}
private static String base64Encode(byte[] bytes) {
return Base64.encodeBase64String(bytes);
}
}
@FeignClient(name = "bsp", url = "${logisticsOrderUrl}",configuration = {BspConfiguration.class})
public interface BspLogisticsOrderSao {
@PostMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-8")
BspLogisticsOrderDTO getLogisticsOrder(LogisticsOrderListModel model);
}
openfeign 使用方法和执行流程的更多相关文章
- 从源码角度看finish()方法的执行流程
1. finish()方法概览 首先我们来看一下finish方法的无参版本的定义: /** * Call this when your activity is done and should be c ...
- 事件之onTouch方法的执行过程 及和 onClick执行发生冲突的解决办法
转载:http://blog.csdn.net/jiangwei0910410003/article/details/17504315#quote 博主推荐: 风萧兮兮易水寒,“天真”一去兮不复还.如 ...
- asyncio源码分析之基本执行流程
基于async关键字的原生协程 # 定义一个简单的原生协程cor async def cor(): print('enter cor') print('exit cor') print(type(co ...
- Android中onTouch方法的执行过程以及和onClick执行发生冲突的解决办法
$*********************************************************************************************$ 博主推荐 ...
- JUC多线程之ThreadPoolExecutor类任务执行流程
ThreadPoolExecutor类: ThreadPoolExecutor是我们最常用的一个线程池类,它实现了AbstractExecutorService接口.首先来看一下它的构造器及相关关键变 ...
- 从HashMap的执行流程开始 揭开HashMap底层实现
心得:如何学习源码: 从某个执行过程入手,建议先从整体入手,了解底层的数据结构是怎么一步一步优化的.最后,在了解完底层的数据结构优化过程后,从重要的核心方法入手,从它的执行流程入手,先去网上搜索了解它 ...
- Dalvik虚拟机java方法执行流程和Method结构体分析
Method结构体是啥? 在Dalvik虚拟机内部,每个Java方法都有一个对应的Method结构体,虚拟机根据此结构体获取方法的所有信息. Method结构体是怎样定义的? 此结构体在不同的andr ...
- django—Form组件校验方法(is_valid)执行流程
1.从is_valid方法入手 def is_valid(self): """Return True if the form has no errors, or Fals ...
- 第二天 ci执行流程
第二天 ci执行流程 welcome 页面 this this->load 单入口框架index.php 两个文件夹 system application定义 定义常亮路径 载入 codeign ...
随机推荐
- MongoDB一些应用知识点
1.在生产环境中至少需要三个节点的复制集架构. 2.在多数的场景中WT引擎比MMAPv1更加出色. 3.要想达到极致的速度,那么一定要给MongoDB足够的内存. 4.避免使用短链接,充分利用连接池, ...
- 《深入理解java虚拟机》读书笔记五——第六章
第六章 类文件结构 1.无关性的基石 各种不同平台的虚拟机与所有平台都统一使用程序存储格式——字节码是构成平台无关的基石. 实现语言无关性的基础仍然是虚拟机和字节码存储格式,Java虚拟机不和包括Ja ...
- c++ stl在竞赛里的使用总结
SET bzoj2761: [JLOI2011]不重复数字 这题... count() 的用法,返回这个值出现的次数,但是在set里只会出现0次和1次,这个可以判断某个值是否在set里出现过 还有si ...
- 异常处理_python
一.异常处理格式: name=[1,2]data={}try: name[3] data['name']except (KeyError,IndexError) as e : #捕捉指定的几个错误类型 ...
- 【Python】表白代码
# -*- coding:utf-8 -*- import turtle import time # 画爱心的顶部 def LittleHeart(): for i in range(200): tu ...
- 解决VMware中Ubuntu18.04全屏问题
在VMware中Ubuntu18.04全屏问题 在虚拟机机中安装完Ubuntu18.04之后界面显示的特别小,默认的好像是800*600分辨率,看着不舒服.先是在设置->设备->显示 ...
- [USACO12DEC]First!
Description Luogu3065 Solution 首先,一个串要是最小的,别的串不能是它的前缀,且和它有相同前缀的串字典序都比他小. Trie树是显然要用的,难点在于如何判断能否最小.其实 ...
- Centos 修改yum源为aliyun
修改服务器源,避免长途跋涉到国外: 位置: vim /etc/yum.repos.d/CentOS-Base.repo aliyun地址: 设置aliyun的yum源 wget -O /etc/yu ...
- 【C/C++】最短路径
BFS求无权图的最短路径 用book数组的值表示路径长度即可,省略 Floyd算法(允许负边) Floyd算法可以一次性求出所有节点之间的最短距离,且代码简单,但是时间复杂度达到了n^3,因此只适用于 ...
- Codeforces Round #601 (Div. 2) E1 Send Boxes to Alice (Easy Version)
#include <bits/stdc++.h> using namespace std; typedef long long ll; ; int a[N]; int n; bool pr ...