Feign自動裝配原理


spring.factories

按照以往的慣例,在研究源碼的時候,我們先看一下spring.factories文件下自動裝配的類FeignAutoConfiguration,其中比較重要的東西有這么幾個

	@Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();


	@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		context.setConfigurations(this.configurations);
		return context;
	}

	@Configuration
	@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
	protected static class HystrixFeignTargeterConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new HystrixTargeter();
		}
	}

	@Configuration
	@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
	protected static class DefaultFeignTargeterConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new DefaultTargeter();
		}
	}
  1. 屬性configurations代表的是各個Feign客戶端的配置類,這個稍后會再次提到
  2. FeignContext這個bean看名字就知道,Feign的上下文環境,包含了所有feign客戶端的配置
  3. 接下來是兩個Targeter是看當前是否存在hystrix環境,接下來也會提到
  4. 除此之外這個類還包含了HttpClient相關的配置就不展開了
@EnableFeignClients注解解析

查看完自動裝配的類,接着看@EnableFeignClients注解

進入這個注解發現,它引入了配置類 FeignClientsRegistrar,由於這個類實現了ImportBeanDefinitionRegistrar接口,所以按照我們以往的經歷直接看一下registerBeanDefinitions方法吧

	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
 		registerFeignClients(metadata, registry);
	}

這里分為了2步

注冊缺省配置
    private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
		// 獲取注解@EnableFeignClients的注解屬性     
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {        

				name = "default." + metadata.getClassName();
			}
			// 各種信息准備就緒,現在執行注冊
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	}
	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

可以看到這里就是處理注冊@EnableFeignClients上defaultConfiguration屬性所指定的客戶端的缺省配置,注意這里配置都是注冊為了FeignClientSpecification類型的bean,這個類型的bean也是本文剛開始提到的被Feign上下文持有的各個Feign客戶端持有的

注冊各個Feign客戶端
public void registerFeignClients(AnnotationMetadata metadata,
                                 BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);
 
    Set<String> basePackages;
 
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
        FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
        : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        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)));
    }
 
    
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner
            .findCandidateComponents(basePackage);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                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"));
                 registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }

這里一共分為3個步驟:

  1. 使用ClassPathScanningCandidateComponentProvider掃描所有的標注了@FeignClient注解的接口
  2. 將注解上包含的屬性作為bean注冊,這些屬性也就是每個Feign客戶端端的配置
  3. @Feign客戶端注冊

private void registerFeignClient(BeanDefinitionRegistry registry,
                                 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    // 1.獲取標注@Feign注解的接口名稱
    String className = annotationMetadata.getClassName();
   
    // 2.使用BeanDefinitionBuilder構造bean:FeignClientFactoryBean
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    
    // 3.添加FeignClientFactoryBean的各個屬性
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    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);
 
    // 4.設置別名
    String alias = name + "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;
    }
 
    // 5.注冊FeignClientFactoryBean
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                                                           new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

上方這個方法通過了5步把各個FeignClient都注冊成了bean:FeignClientFactoryBean,相信看過之前文章的同學都知道FactoryBean系列的bean是干什么的了。而Feign整合Ribbon和Hystrix的核心應該也在這個類里面了

原文地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM