这期内容当中小编将会给大家带来有关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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。