环境:springboot2.2.13.RELEASE + springcloud Hoxton.SR8
1 开启Feign功能
("com.pack.feign")public class BaseApplication extends SpringBootServletInitializer {protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(BaseApplication.class);}public static void main(String[] args) {SpringApplication.run(BaseApplication.class, args);}}
查看EnableFeignClients源码
public EnableFeignClients {}
@Import导入了其它类,关于@Import的使用已经介绍过了。直接查看FeignClientsRegistrar
查看核心代码
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {registerDefaultConfiguration(metadata, registry);registerFeignClients(metadata, registry);}
registerFeignClients方法用来扫描指定包下带有@FeignClient注解的类然后注册为Bean。
2 注册FeignClientBean
扫描及注册Bean
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;// 获取添加注解@EnableFeignClients时的相关配置信息Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());// 设置过滤条件,只匹配带有@FeignClient的注解AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {// 过滤条件添加到Scanner中scanner.addIncludeFilter(annotationTypeFilter);// 获取设置的扫描包信息basePackages = getBasePackages(metadata);}else {final Set<String> clientClasses = new HashSet<>();basePackages = new HashSet<>();for (Class<?> clazz : clients) {basePackages.add(ClassUtils.getPackageName(clazz));clientClasses.add(clazz.getCanonicalName());}AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {@Overrideprotected boolean match(ClassMetadata metadata) {String cleaned = metadata.getClassName().replaceAll("\\$", ".");return clientClasses.contains(cleaned);}};scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}// 根据设置的包路径,查找符合条件的组件Beanfor (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = getClientName(attributes);registerClientConfiguration(registry, name,attributes.get("configuration"));// 真正注册BeanregisterFeignClient(registry, annotationMetadata, attributes);}}}}
过滤符合条件的Class
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}}}}}return candidates;}
查找到所有符合条件的Bean后,接下来就是注册Bean
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();beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);// has a default, won't be nullboolean primary = (Boolean) attributes.get("primary");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);}
注意下面这行代码
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
FeignClientFactoryBean是一个工厂Bean,实现了FactoryBean。仅凭一个接口就能调用方法肯定是被代理了。
到这里我们的Feign 相关的Bean就已经注册完了。接下来看看在执行过程中是如何使用的。
3 Feign如何被注入
准备业务代码
public interface MedicareSettingFeign {public R queryRoles( InsuranceManageQueryDTO queryDTO) ;}public class FeignDemoController {private MedicareSettingFeign feign ;public Object invoke(InsuranceManageQueryDTO dto) {return feign.queryRoles(dto) ;}}
当容器在实例化FeignDemoController时,是通过
CommonAnnotationBeanPostProcessor注入MedicareSettingFeign的,接下来查看这个过程
3.1 获取Bean对象
DefaultListableBeanFactory
最后进入到
AbstractAutowireCapableBeanFactory类中的如下方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args)throws BeanCreationException {// ...try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}// ...}
3.1.1 填充Bean属性
populateBean方法填充属性
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {// 核心代码for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}}
这里主要就是执行
CommonAnnotationBeanPostProcessor处理器的postProcessProperties来处理属性注入。
注入方法
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}}
通过反射设置Bean的属性
protected void inject(Object target, String requestingBeanName, PropertyValues pvs)throws Throwable {if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);field.set(target, getResourceToInject(target, requestingBeanName));} else {// ...}}
target为目标对象(Controller),getResourceToInject方法获取要注入的资源对象
protected Object getResourceToInject(Object target, String requestingBeanName) {return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));}
进入getResource
protected Object getResource(LookupElement element, String requestingBeanName) throws NoSuchBeanDefinitionException {return autowireResource(this.resourceFactory, element, requestingBeanName);}
一路调试最后会进入到AbstractBeanFactory
接着进入到
doGetObjectFromFactoryBean方法调用FactoryBean的getObject方法
3.1.2 获取Feign接口的代理对象
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {Object object;try {if (System.getSecurityManager() != null) {AccessControlContext acc = getAccessControlContext();try {object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);}catch (PrivilegedActionException pae) {throw pae.getException();}}else {object = factory.getObject();}}// ...return object;}
进入到getObject方法
public Object getObject() throws Exception {return getTarget();}<T> T getTarget() {FeignContext context = applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(url)) {if (!name.startsWith("http")) {url = "http://" + name;}else {url = name;}url += cleanPath();return (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));}if (StringUtils.hasText(url) && !url.startsWith("http")) {url = "http://" + url;}String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return (T) targeter.target(this, builder, context,new HardCodedTarget<>(type, name, url));}
通过loadBalance方法最后进入到ReflectiveFeign中
3.1.3 创建代理对象
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;}
通过JDK创建动态代理对象,其中InvocationHandler使用的是HystrixInvocationHandler对象;通过这个Handler名称也知道Feign调用是具有熔断功能的。
final class HystrixInvocationHandler implements InvocationHandler {public Object invoke(final Object proxy, final Method method, final 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();}HystrixCommand<Object> hystrixCommand =new HystrixCommand<Object>(setterMethodMap.get(method)) {protected Object run() throws Exception {try {return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);} catch (Exception e) {throw e;} catch (Throwable t) {throw (Error) t;}}protected 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());}}};if (Util.isDefault(method)) {return hystrixCommand.execute();} else if (isReturnsHystrixCommand(method)) {return hystrixCommand;} else if (isReturnsObservable(method)) {// Create a cold Observablereturn hystrixCommand.toObservable();} else if (isReturnsSingle(method)) {// Create a cold Observable as a Singlereturn hystrixCommand.toObservable().toSingle();} else if (isReturnsCompletable(method)) {return hystrixCommand.toObservable().toCompletable();} else if (isReturnsCompletableFuture(method)) {return new ObservableCompletableFuture<>(hystrixCommand);}return hystrixCommand.execute();}}
这里Feign的代理对象就创建完成并且注入到了Controller中。
4 Feign接口调用
所有接口调用的入口都是在HystrixInvocationHandler中的invoke方法中
HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
这里的invoke是调用的SynchronousMethodHandler中的方法
public Object invoke(Object[] argv) throws Throwable {RequestTemplate template = buildTemplateFromArgs.create(argv);Options options = findOptions(argv);Retryer retryer = this.retryer.clone();while (true) {try {return executeAndDecode(template, options);} 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;}}}
最终业务的执行
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request = targetRequest(template);if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}Response response;long start = System.nanoTime();try {response = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 12response = response.toBuilder().request(request).requestTemplate(template).build();} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));}throw errorExecuting(request, e);}long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);if (decoder != null)return decoder.decode(response, metadata.returnType());CompletableFuture<Object> resultFuture = new CompletableFuture<>();asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,metadata.returnType(),elapsedTime);try {if (!resultFuture.isDone())throw new IllegalStateException("Response handling not done");return resultFuture.join();} catch (CompletionException e) {Throwable cause = e.getCause();if (cause != null)throw cause;throw e;}}
该方法是真正执行请求的地方。这里还实现了负载均衡。
完毕!!!
给个关注+转发呗谢谢
公众:Springboot实战案例锦集






