您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Feign中EnableFeignClients的作用是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
springcloud-openfeign-core-2.1.1.release.
在springcloud中使用feign的時候,如下List-1,FeignClient的name是服務名稱,會自動從Eureka中獲取物理地址。
List-1
@FeignClient(name="UserProvider") public interface UserProvider { ... }
Springcloud中這個是如何實現的?
這個要從@EnableFeignClients入手,如下List-2,Import注解引入了FeignClientsRegistrar——實現了ImportBeanDefinitionRegistrar接口,這樣springboot會處理這個FeignClientsRegistrar。
List-2
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {}; }
如下圖1所示,FeignClientsRegistrar并沒有復雜的繼承關系,重點類看ImportBeanDefinitionRegistrar的registerBeanDefinitions實現。
圖1
如下List-3,分倆個步驟,首先是registerDefaultConfiguration方法,將EnableFeignClients的defaultConfiguration注冊到Spring容器中;之后是registerFeignClients方法將FeignClient注解的接口注冊到Spring容器中。
List-3
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }
來看registerDefaultConfiguration方法,如下List-4,
獲取EnableFeignClients的所有屬性,之后如果含有defaultConfiguration,則將defaultConfiguration注冊到Spring容器中
方法registerClientConfiguration中,用Builder模式,構造FeignClientSpecification類型的BeanDefinition。FeignClientSpecification實現了NamedContextFactory.Specification接口,屬性有個name和Class<?>類型的configuration。
List-4
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { 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()); }
registerFeignClients方法中,實現則較為復雜,如下List-5
List-5
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner();//1 scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName());//2 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class);//3 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) {//4 scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else {//5 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) { // 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"));//6 registerFeignClient(registry, annotationMetadata, attributes);//7 } } } }
1處獲取ClassPathScanner,用于掃描類路徑
2處獲取EnableFeignClients的所有屬性
3處構造一個AnnotationTypeFilter,構造方法參數是FeignClient,這個用于過濾出只含有FeignClient的類
獲得EnableFeignClients的clients屬性值,4處如果是空,則獲得EnableFeignClients所在的package路徑(如果沒有設置basePackageClasses)
5處,即EnableFeignClients的clients屬性不是空,則遍歷,放入集合中,同時獲取client所在的package路面,加入到basePacakges中;構造AbstractClassTestingTypeFilter,這是增加一個過濾條件,即標FeignClient注解的接口,必須在EnableFeignClients的clients中
遍歷basePackages,獲取每個package下的符合條件的類,得到對應的beanDefinition,6處得到FeignClient的configuration值,通過FeignClientSpecification其注冊到spring容器中,有意思的是這里檢查了FeignClient注解的類須是接口,不然會報錯。
7處將FeignClient注解的接口封裝到FeignClientFactoryBean中,FactoryBean大家懂的,Spring中接口都封裝到這個里面。
上述就是小編為大家分享的Feign中EnableFeignClients的作用是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。