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",2 url = "${nuts.sms.smsNoticeUrl}",configuration = {HttpClientProxyConfiguration.class}) public interface NoticeSao { @PostMapping("/tesyt") @Async ResponseDTO sendSms(NoticeDTO noticeDTO,1 URI uri); } public void apply(RequestTemplate template) { // 3 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); }