SpringCloud之feign的使用以及源码解析
feign的作用
Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方
法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不
需要通过常规的 Http Client 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法
一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
feign的使用
main方法上加上注解@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderMain83.class,args);
}
}
定义自己的service
@Component
@FeignClient(value = "cloudalibaba-nacos-provider")
public interface PaymentService {
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
定义自己的controller
@RestController
public class FeignController {
@Autowired
private PaymentService paymentService;
@GetMapping(value = "nacos/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentService.getPaymentById(id);
}
}
通过这两个步骤,就可以像调本地接口一样调用远程服务了
feign源码解析
代码整体流程图
EnableFeignClients注解的解析
先从@EnableFeignClients注解看起
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
import了一个FeignClientsRegistrar
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
FeignClientsRegistrar继承了ImportBeanDefinitionRegistrar,所以我们看FeignClientsRegistrar的registerBeanDefinitions方法,看看注入了什么BeanDefinition
FeignClientsRegistrar#registerFeignClients方法
首先拿到注解EnableFeignClients的属性
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
new了一个注解的过滤器
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
然后就拿到需要扫描的包
basePackages = getBasePackages(metadata);
然后是一个for循环,遍历basePackages,然后是根据包找到有FeignClient注解的Components
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
然后就到了注册这个BeanDefinition的方法了registerFeignClient(registry, annotationMetadata, attributes);
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
一长串的definition的设置属性,有url,path,fallback等。
其中关键的是
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
bean的类型被改成了FeignClientFactoryBean。
Bean的初始化
先看一下FeignClientFactoryBean
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
FeignClientFactoryBean实现了FactoryBean,说明bean是自定义实例化的,直接看getObject方法
@Override
public Object getObject() throws Exception {
return getTarget();
}
getObject方法有调用了getTarget方法,直接return值,return的方法是
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
看loadBalance方法,
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
//把LoadBalancerFeignClient放进去
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
根据context拿到targeter,执行targeter.target(this, builder, context, target);
由于没引入Hystrix,只引入了feign,所以直接调用feign.target(target)
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
进入feign.target
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
先进入build方法
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
构造了一些属性,然后返回了new出来ReflectiveFeign返回,然后再进入newInstance方法
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
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
//拿到InvocationHandler
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;
}
先解析了这个类里面所有的方法
放进去methodToHandler
得到InvocationHandler (FeignInvocationHandler)
生成动态代理
生成的methodToHandler
生成的动态代理类
就这样bean就被创建出来了。
方法的调用
之前了解过JDK动态代理的会知道,调用被代理类的方法时,会先进入InvocationHandler 的invoke方法,也就是FeignInvocationHandler的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
然后就会调用dispatch.get(method).invoke(args)
dispatch就是生成ReflectiveFeign时放进去的SynchronousMethodHandler,接下来就会调用
SynchronousMethodHandler的executeAndDecode
Request request = targetRequest(template);
response = client.execute(request, options);
生成请求,调用client的execute方法,client就是FeignClientFactoryBean的loadBalance放进去的LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
然后就去到LoadBalancerFeignClient的execute方法
然后就是整合ribbon去实现负载均衡,拼装request,去调用远程方法。