目录
一、前言
二、注册FeignClient
1、 registerBeanDefinitions()逻辑
2、注册默认配置
2.1、registerDefaultConfiguration()逻辑
2.2、registerClientConfiguration()逻辑
3、注册FeignClient(一般是包下)
3.1、getBasePackages(metadata)逻辑
3.2、扫描包下的FeignClient
3.3、遍历扫描出来的候选组件,开始调用注册逻辑
现在许许多多的大大小小公司都使用了微服务框架,对程序员的要求不再是仅了解,面试的时候更是直面底层原理。要想深得面试官的青睐,还是得准备准备读读源码,亲自动动手探究一番方可临危不乱。读源码对面试有帮助,对理解框架设计也可提升你的认知,对日后碰到框架扩展的需求可以积累经验以及节约时间等。
从上一节可见我们来到了spring-cloud-openfeign的FeignClientsRegistrar组件的 registerBeanDefinitions()处理逻辑。下面我们沿着它继续跟进
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
这里主要将相应的注册逻辑封装了出去,根据上一节解析的注解元数据(StandardAnotationMetadata)以及注册机(DefaultListableBeanFactory)完成默认配置的注册、对应模块下FeignClients的注册。
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 获取@EnableFeignClients全部属性
Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
// 如:default.com.ceam.AdminApp
name = "default." + metadata.getClassName();
}
// 注册
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
拼接name,然后再调用registerClientConfiguration()完成注册。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
// 构建Bean定义建造者
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
// 注册到DefaultListableBeanFactory
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
主要构建Bean定义,然后注册到DefaultListableBeanFactory(Bean定义注册中心)。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet candidateComponents = new LinkedHashSet<>();
// 获取@EnableFeignClients的全部属性
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
// 获取@EnableFeignClients的clients属性,如果不是空的,则禁用类路径扫描。
final Class>[] clients = attrs == null ? null : (Class>[]) attrs.get("clients");
// 使用路径扫描
if (clients == null || clients.length == 0) {
// 获取扫描器,Spring在处理@RestController、@Service、@Component等时也用到该扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
// 设置资源加载器
scanner.setResourceLoader(this.resourceLoader);
// 设置Filter,只扫描@FeignClient注解
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
// 从@EnableFeignClients中获取扫描包路径
Set basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
// 根据指定包路径扫描@FeignClient标注的接口,添加到candidateComponents集合
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// 不用类路径扫描
for (Class> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// 遍历candidateComponents
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface验证带注释的类是一个接口
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
// @FeignClient 只能在接口上指定
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
// 获取@FeignClient的属性
Map attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
// 获取@FeignClient中指定的服务名
String name = getClientName(attributes);
registerClientConfiguration(registry, name, attributes.get("configuration"));
// 注册FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
主要逻辑:
- 获取@EnableFeignClients注解的属性,重点拿到扫描包路径(basePackages)。
- 然后通过getScanner()方法获取扫描器:ClassPathScanningCandidateComponentProvider。
- 接着给扫描器ClassPathScanningCandidateComponentProvider添加一个注解过滤器(AnnotationTypeFilter),只过滤出包含@FeignClient注解的BeanDefinition。
- 再通过getBasePackages(metadata)方法获取@EnableFeingClients注解中的指定的包扫描路径 或 扫描类;如果没有获取到,则默认扫描启动类所在的包路径。
- 然后进入到扫描逻辑:通过scanner.findCandidateComponents(basePackage)方法从包路径下扫描出所有标注了@FeignClient注解并符合条件装配的接口;组件内部通过isCandidateComponent(metadataReader)做过滤处理,重点是根据设置的过滤器过滤。
- 最后将FeignClientConfiguration (主要是服务名称)在BeanDefinitionRegistry中注册一下,再对FeignClient做真正的注册操作。最终注册到DefaultListableBeanFactory。
protected Set getBasePackages(AnnotationMetadata importingClassMetadata) {
// 获取@EnableFeignClients的属性
Map attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
Set basePackages = new HashSet<>();
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
// 指定包路径
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
// 指定类名情况下,通过ClassUtils获取指定类所在的包
for (Class> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果上面两种情况下都没有获取到包路径,则按照启动类所在的包作为扫描路径
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
没有指定包路径或扫描类,则通过ClassUtils获取启动类所在路径。
将包路径传递进来,开始扫描指定包下资源。
1)findCandidateComponents()逻辑
public Set findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
// 999520一般走这里
return scanCandidateComponents(basePackage);
}
}
扫描候选组件的类路径。
2)scanCandidateComponents(basePackage)
3)过滤出@FeignClient的.class资源
若匹配返回true。
4)添加到candidates集合
封装Bean定义,将封装好的Bean定义以及注册机传进来,调用BeanDefinitionReaderUtils注册。
BeanDefinitionReaderUtils工具类主要是交给注册机(DefaultListableBeanFactory)完成注册,它还可以对BeanName注册别名如果指定的话。
最终还是注册到Bean定义注册中心DefaultListableBeanFactory的beanDefinitionMap字段。