交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
Fegin 第1 章:启动FeignClientsRegistrar
分享
未结
0
1113
李延
LV6
2021-06-19
悬赏:20积分
# 作用 我们在使用feign时,都需要加EnableFeignClients注解,我们就以它为入口看一下feign是如何初始化的。 # 加载 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { ``` 在注解上我们看到了Import,并且它的值FeignClientsRegistrar是ImportBeanDefinitionRegistrar子类。所有我们具体看一下这个类 # ImportBeanDefinitionRegistrar 在我们分析springboot代码的时候知道,spring会通过它的registerBeanDefinitions方法加载bena定义。 ## registerBeanDefinitions ```java @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); } ``` 这里有2个方法。 1. registerDefaultConfiguration 2. registerFeignClients 其中registerDefaultConfiguration是feign的配置,每个feign配置就是一个独立的spring上下文,而且我们每个feign类都有一个自己独立的配置,这个过程我们之后说明。 registerFeignClients就是加载fegin的过程。我们先来解析 ## registerFeignClients ```java public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 获取EnableFeignClients的注解 LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>(); 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"); //创建ClassPathScanningCandidateComponentProvider对象,扫描指定路径,并将有FeignClient注解的类进行加载 if (clients == null || clients.length == 0) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); Set<String> basePackages = getBasePackages(metadata); for (String basePackage : basePackages) { candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); } } else { for (Class<?> clazz : clients) { candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz)); } } 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); //为当前扫描到的Fegin添加配置,并将其注册到registry中 registerClientConfiguration(registry, name, attributes.get("configuration")); //将fegin注册到registry中 registerFeignClient(registry, annotationMetadata, attributes); } } } ``` 在这里我们看到通过ClassPathScanningCandidateComponentProvider对象扫描到所有包含FeginClient注解的class,之后将这些class一方面将bean定义注册到registry中,另一方面为它生成一个自己的配置也注册到registry中。 这里我们先看注册bean定义的过程 ```java 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); } ``` 这里代码很简单,就是读取注解值将其设置为bean定义并进行注册,其中我们需要关注的是,生成的bean定义其真是的class是FeignClientFactoryBean对象。 ## registerClientConfiguration 在前面我们分析知道,在最开始有一个默认的fegin配置,并且每个fegin类有一个单独的配置。同时它们都通过registerClientConfiguration注册到了BeanDefinitionRegistry中。 ### registerClientConfiguration 这个方法是加载默认的fegin配置 ```java 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")); } } ``` 我们看到它最后调用的也是registerClientConfiguration方法,而name使用的是"default."+ 启动类的全路径 ### registerClientConfiguration ```java 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()); } ``` 这里我们其实它注册的就是FeignClientSpecification对象。其中有一个configuration参数。这个参数的值是根据我们Fegin对应的注解来指定的。这个类的具体作用我们再对应章节说明
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号