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 ...
随机推荐
- java 日期累加,如何求一年后日期,一月后日期,一周后日期和一天后日期
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import ja ...
- VSCode常用插件之vscode-fileheader使用
更多VSCode插件使用请访问:VSCode常用插件汇总 vscode-fileheader这是一个给js文件(html.css也可以使用,但是没意义!!!)生成头部注释的插件,每次修改js文件之后会 ...
- 实用沙盒工具 —— VMware Workstation15安装教程
一:简介 VMware Workstation(中文名"威睿工作站")是一款功能强大的桌面虚拟计算机软件,提供用户可在单一的桌面上同时运行不同的操作系统,和进行开发.测试 .部署新 ...
- UTF-8与GBK的区别
中文解码提示UnicodeDecodeError,UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0: inv ...
- linux基础安全
一.用户防护 Chage -l 用户名 查看用户账号有效期信息 Chage -E 时间 用户名 给用户设置过期时间 /etc/login.defs 文件保存许多默认设置 Pas ...
- javaWeb快速入门+——初体验-HelloWorld
文章转载自 https://www.cnblogs.com/1906859953Lucas/p/10821840.html 练习成品下载 https://www.lanzous.com/i9fljkj ...
- [TJOI2015] 线性代数 - 最大权闭合子图
展开 \(D=(AB-C)A^T\\ =\sum_{i=1}^n(\sum_{j=1}^na_jb_{j,i}-c_i)a_i\\ =\sum_{i=1}^n\sum_{j=1}^na_ia_jb_{ ...
- spring boot MySQL Public Key Retrieval is not allowed
建议在链接url处添加对应的属性 jdbc:mysql://localhost:3306/book?allowPublicKeyRetrieval=true&useSSL=false
- DataGridView 定位到指定行
//定位到指定行(样式)dataGridView1.ClearSelection();dataGridView1.Rows[selectIndex].Selected = true; //让指定行处于 ...
- C语言 栈的初始化,入栈,出栈,判断栈是否为空,清空栈等函数
#include <stdio.h>#include <stdlib.h> typedef struct Node{ int data ; struct Node * pNex ...