大数跨境
0
0

SpringCloud Feign实现原理源分析

SpringCloud Feign实现原理源分析 Spring全家桶实战案例
2021-11-22
1
导读:SpringCloud Feign实现原理源分析

环境:springboot2.2.13.RELEASE + springcloud Hoxton.SR8


1 开启Feign功能

@SpringCloudApplication@ServletComponentScan@EnableFeignClients("com.pack.feign")public class BaseApplication extends SpringBootServletInitializer {  @Override  protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {    return builder.sources(BaseApplication.class);  }
public static void main(String[] args) { SpringApplication.run(BaseApplication.class, args); }}

查看EnableFeignClients源码

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)public @interface EnableFeignClients {}

@Import导入了其它类,关于@Import的使用已经介绍过了。直接查看FeignClientsRegistrar

查看核心代码

@Overridepublic 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() { @Override protected boolean match(ClassMetadata metadata) { String cleaned = metadata.getClassName().replaceAll("\\$", "."); return clientClasses.contains(cleaned); } }; scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } // 根据设置的包路径,查找符合条件的组件Bean for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition 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")); // 真正注册Bean registerFeignClient(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 null boolean 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如何被注入

准备业务代码

@FeignClient(value = "his-insurance")public interface MedicareSettingFeign {    @GetMapping("/medicareSetting/queryInsuranceManageInfo")    public R queryRoles(@RequestParam InsuranceManageQueryDTO queryDTO)  ;}@RestController@RequestMapping("/feign")public class FeignDemoController {    @Resource    private MedicareSettingFeign feign ;    @GetMapping("/invoke")    public Object invoke(InsuranceManageQueryDTO dto) {        return feign.queryRoles(dto) ;    }}

当容器在实例化FeignDemoController时,是通过
CommonAnnotationBeanPostProcessor注入MedicareSettingFeign的,接下来查看这个过程

3.1 获取Bean对象

DefaultListableBeanFactory

最后进入到
AbstractAutowireCapableBeanFactory类中的如下方法

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable 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, @Nullable 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, @Nullable String beanName, @Nullable 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, @Nullable String requestingBeanName, @Nullable 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, @Nullable String requestingBeanName) {    return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));}

进入getResource

protected Object getResource(LookupElement element, @Nullable 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方法

@Overridepublic 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 unwrap client = ((LoadBalancerFeignClient) client).getDelegate(); } if (client instanceof FeignBlockingLoadBalancerClient) { // not load balancing because we have a url, // but Spring Cloud LoadBalancer is on the classpath, so unwrap client = ((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 {  @Override  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)) { @Override 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; } }
@Override 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 Observable return ((Observable) result).toBlocking().first(); } else if (isReturnsSingle(method)) { // Create a cold Observable as a Single return ((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 interface throw new AssertionError(e); } catch (InvocationTargetException | ExecutionException e) { // Exceptions on fallback are tossed by Hystrix throw new AssertionError(e.getCause()); } catch (InterruptedException e) { // Exceptions on fallback are tossed by Hystrix Thread.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 Observable return hystrixCommand.toObservable(); } else if (isReturnsSingle(method)) { // Create a cold Observable as a Single return 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中的方法

@Overridepublic 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 12 response = 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实战案例锦集


【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读38
粉丝0
内容832