feign是springCloud微服务之间远程调用的一个组件。首先从@EnableFeignClients注解看起,@EnableFeignClients注解注入了一个FeignClientsRegistrar类,FeignClientsRegistrar它是一个注册器,看名字就能知道,通过扫描某些特性的类注册成spring的Bean。

    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.TYPE)
    3. @Documented
    4. @Import(FeignClientsRegistrar.class)
    5. public @interface EnableFeignClients {
    6. }
    1. class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
    2. ResourceLoaderAware, EnvironmentAware {
    3. }

    FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar,ResourceLoaderAware,EnvironmentAware这几个接口。

    • ImportBeanDefinitionRegistrar:实现这个接口的bean可以手动注册Bean到spring容器中;
    • ResourceLoaderAware:支持可以classPath下的配置文件;
    • EnvironmentAware:可以读取spring的全局配置信息。

    下面看下实现ImportBeanDefinitionRegistrarregisterBeanDefinitions方法

    1. @Override
    2. public void registerBeanDefinitions(AnnotationMetadata metadata,
    3. BeanDefinitionRegistry registry) {
    4. // 读取自定义的配置文件信息,如果没有定义则读取默认的配置
    5. registerDefaultConfiguration(metadata, registry);
    6. // 注册FeignClient
    7. registerFeignClients(metadata, registry);
    8. }
    1. public void registerFeignClients(AnnotationMetadata metadata,
    2. BeanDefinitionRegistry registry) {
    3. // 拿到一个扫描器
    4. ClassPathScanningCandidateComponentProvider scanner = getScanner();
    5. scanner.setResourceLoader(this.resourceLoader);
    6. Set<String> basePackages;
    7. Map<String, Object> attrs = metadata
    8. .getAnnotationAttributes(EnableFeignClients.class.getName());
    9. // 添加一个过滤器,过滤添加了@FeignClient注解的类
    10. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
    11. FeignClient.class);
    12. // 查看EnableFeignClients注解有没有配置clients属性,如果没有配置则扫描加了@FeignClient注解的类
    13. final Class<?>[] clients = attrs == null ? null
    14. : (Class<?>[]) attrs.get("clients");
    15. if (clients == null || clients.length == 0) {
    16. scanner.addIncludeFilter(annotationTypeFilter);
    17. basePackages = getBasePackages(metadata);
    18. }
    19. else {
    20. final Set<String> clientClasses = new HashSet<>();
    21. basePackages = new HashSet<>();
    22. for (Class<?> clazz : clients) {
    23. basePackages.add(ClassUtils.getPackageName(clazz));
    24. clientClasses.add(clazz.getCanonicalName());
    25. }
    26. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
    27. @Override
    28. protected boolean match(ClassMetadata metadata) {
    29. String cleaned = metadata.getClassName().replaceAll("\\$", ".");
    30. return clientClasses.contains(cleaned);
    31. }
    32. };
    33. scanner.addIncludeFilter(
    34. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    35. }
    36. for (String basePackage : basePackages) {
    37. Set<BeanDefinition> candidateComponents = scanner
    38. .findCandidateComponents(basePackage);
    39. // 遍历所有候选的BeanDefinition
    40. for (BeanDefinition candidateComponent : candidateComponents) {
    41. if (candidateComponent instanceof AnnotatedBeanDefinition) {
    42. // verify annotated class is an interface
    43. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
    44. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
    45. Assert.isTrue(annotationMetadata.isInterface(),
    46. "@FeignClient can only be specified on an interface");
    47. // 查找FeignClient注解身上设置的属性
    48. Map<String, Object> attributes = annotationMetadata
    49. .getAnnotationAttributes(
    50. FeignClient.class.getCanonicalName());
    51. String name = getClientName(attributes);
    52. // 注册FeignClient的配置文件的信息
    53. registerClientConfiguration(registry, name,
    54. attributes.get("configuration"));
    55. // 注册bean
    56. registerFeignClient(registry, annotationMetadata, attributes);
    57. }
    58. }
    59. }
    60. }
    1. private void registerFeignClient(BeanDefinitionRegistry registry,
    2. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    3. String className = annotationMetadata.getClassName();
    4. BeanDefinitionBuilder definition = BeanDefinitionBuilder
    5. .genericBeanDefinition(FeignClientFactoryBean.class);
    6. validate(attributes);
    7. definition.addPropertyValue("url", getUrl(attributes));
    8. definition.addPropertyValue("path", getPath(attributes));
    9. String name = getName(attributes);
    10. definition.addPropertyValue("name", name);
    11. definition.addPropertyValue("type", className);
    12. definition.addPropertyValue("decode404", attributes.get("decode404"));
    13. definition.addPropertyValue("fallback", attributes.get("fallback"));
    14. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    15. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    16. String alias = name + "FeignClient";
    17. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    18. boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
    19. beanDefinition.setPrimary(primary);
    20. String qualifier = getQualifier(attributes);
    21. if (StringUtils.hasText(qualifier)) {
    22. alias = qualifier;
    23. }
    24. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
    25. new String[] { alias });
    26. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    27. }

    上面registerFeignClient方法主要是将@FeignClient注解中的参数值构造成一个FeignClientFactoryBean的BeanDefinition,然后客户端注入那个Bean时,比如通过@Autowired注入时会调用FactoryBean的getObject方法拿到Bean对象。

    1. @Override
    2. public Object getObject() throws Exception {
    3. return getTarget();
    4. }
    1. <T> T getTarget() {
    2. FeignContext context = applicationContext.getBean(FeignContext.class);
    3. Feign.Builder builder = feign(context);
    4. // 如果没有传入url,直传了name,将会根据集群名字利用ribbon的负载均衡调用
    5. // 如果既传入了url又传入了name,将会之间根据url调用
    6. if (!StringUtils.hasText(this.url)) {
    7. String url;
    8. if (!this.name.startsWith("http")) {
    9. url = "http://" + this.name;
    10. }
    11. else {
    12. url = this.name;
    13. }
    14. url += cleanPath();
    15. return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
    16. this.name, url));
    17. }
    18. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    19. this.url = "http://" + this.url;
    20. }
    21. String url = this.url + cleanPath();
    22. Client client = getOptional(context, Client.class);
    23. if (client != null) {
    24. if (client instanceof LoadBalancerFeignClient) {
    25. // not load balancing because we have a url,
    26. // but ribbon is on the classpath, so unwrap
    27. client = ((LoadBalancerFeignClient)client).getDelegate();
    28. }
    29. builder.client(client);
    30. }
    31. Targeter targeter = get(context, Targeter.class);
    32. // 会利用jdk动态代理生成代理对象返回出去
    33. return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
    34. this.type, this.name, url));
    35. }

    上面的getTarget方法将会利用@FeginClient注解的参数进行封装,然后利用jdk动态代理返回一个Object出去。

    1. @Override
    2. public <T> T newInstance(Target<T> target) {
    3. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    4. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    5. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    6. for (Method method : target.type().getMethods()) {
    7. if (method.getDeclaringClass() == Object.class) {
    8. continue;
    9. } else if(Util.isDefault(method)) {
    10. DefaultMethodHandler handler = new DefaultMethodHandler(method);
    11. defaultMethodHandlers.add(handler);
    12. methodToHandler.put(method, handler);
    13. } else {
    14. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    15. }
    16. }
    17. InvocationHandler handler = factory.create(target, methodToHandler);
    18. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
    19. for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    20. defaultMethodHandler.bindTo(proxy);
    21. }
    22. return proxy;
    23. }

    上面主要流程:

    • 通过@EnableFeignClients注解上的@Import注入的FeignClientsRegistrar类,拿到一个扫描器,如果在EnableFeignClients注解上设置了clients属性则不扫描,如果没有设置则扫描加了@FeignClient的注解;
    • 遍历所有加了@FeignClient的注解的类,每个FeignClient都会生成一个FeignClientFactoryBean的BeanDefinition,然后注册到spring容器中;
    • 当客户端注入这个加了@FeignClient的类的对象时,将会调用FactoryBean的getObject方法,FactoryBean的getObject方法返回的就是当前Bean的实例;
    • 然后这个Bean是将@FeignClient注解的参数利用jdk动态代理生成的。