FeignClient 扫描
FeignClientsRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
// 注册默认配置
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
/**
* 默认配置描述如下* A custom <code>@Configuration</code> for all feign clients. Can contain override* <code>@Bean</code> definition for the pieces that make up the client, for instance* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.** @see FeignClientsConfiguration for the defaults* @return list of default configurations*/
Class<?>[] defaultConfiguration() default {};
FeignClientsRegistrar#registerFeignClients
ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));Set<String> basePackages = getBasePackages(metadata);for (String basePackage : basePackages) {// 获取候选类 被FeignClient标注 candidateComponents.addAll(scanner.findCandidateComponents(basePackage));}
FeignClientsRegistrar#registerClientConfiguration
FeignClientsRegistrar#registerFeignClient
// 这段逻辑会在真正获取对象的时候调用
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {factoryBean.setUrl(getUrl(beanFactory, attributes));factoryBean.setPath(getPath(beanFactory, attributes));factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));Object fallback = attributes.get("fallback");if (fallback != null) {factoryBean.setFallback(fallback instanceof Class? (Class<?>) fallback: ClassUtils.resolveClassName(fallback.toString(), null));}Object fallbackFactory = attributes.get("fallbackFactory");if (fallbackFactory != null) {factoryBean.setFallbackFactory(fallbackFactory instanceof Class? (Class<?>) fallbackFactory: ClassUtils.resolveClassName(fallbackFactory.toString(),null));}// 获取实例对象的核心逻辑return factoryBean.getObject();});definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);definition.setLazyInit(true);validate(attributes);
我们断点打在factoryBean.getObject() 追进去
return (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));
然后继续往下走:HystrixTargeter -> Feign -> 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);// JDK 动态代理创建了真正的对象 handler类为HystrixInvocationHandler implements InvocationHandlerT proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}
调用的时候
HystrixCommand<Object> hystrixCommand =new HystrixCommand<Object>(setterMethodMap.get(method)) {@Overrideprotected Object run() throws Exception {try {// 这里真正的去调用远程方法return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);} catch (Exception e) {throw e;} catch (Throwable t) {throw (Error) t;}}// 这里是异常处理@Overrideprotected Object getFallback() {if (fallbackFactory == null) {return super.getFallback();}try {Object fallback = fallbackFactory.create(getExecutionException());Object result = fallbackMethodMap.get(method).invoke(fallback, args);if (isReturnsHystrixCommand(method)) {return ((HystrixCommand) result).execute();} else if (isReturnsObservable(method)) {// Create a cold Observablereturn ((Observable) result).toBlocking().first();} else if (isReturnsSingle(method)) {// Create a cold Observable as a Singlereturn ((Single) result).toObservable().toBlocking().first();} else if (isReturnsCompletable(method)) {((Completable) result).await();return null;} else if (isReturnsCompletableFuture(method)) {return ((Future) result).get();} else {return result;}} catch (IllegalAccessException e) {// shouldn't happen as method is public due to being an interfacethrow new AssertionError(e);} catch (InvocationTargetException | ExecutionException e) {// Exceptions on fallback are tossed by Hystrixthrow new AssertionError(e.getCause());} catch (InterruptedException e) {// Exceptions on fallback are tossed by HystrixThread.currentThread().interrupt();throw new AssertionError(e.getCause());}}};
我们在调用的时候在选择实例NacosNamingService#selectInstances,发现会选择实例,这就是 OpenFeign 首次发起远程调用时,会很慢的原因。
Feign 隔离
源代码
org.springframework.cloud.openfeign.FeignClientFactoryBean#get
org.springframework.cloud.context.named.NamedContextFactory#getInstance
public <T> T getInstance(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);try {return context.getBean(type);}catch (NoSuchBeanDefinitionException e) {// ignore}return null;
}
org.springframework.cloud.context.named.NamedContextFactory#getContext
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}
org.springframework.cloud.context.named.NamedContextFactory#createContext
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();if (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}for (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);// jdk11 issue// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101context.setClassLoader(this.parent.getClassLoader());}context.setDisplayName(generateDisplayName(name));// 重点在这里,创建了一个迷你的上下文对象,并且刷新了迷你上下文对象进行实例化操作 这会影响OpenFeing的效率变慢context.refresh();return context;}