1、开启FeignClient功能
我们在 app-b应用的主启动类AppBMain 上有个 @EnableFeignClients
注解,我们知道注解 @Enablexxxx 开启xxx功能,一般在这类的注解上都有@Import的注解,同样 EnableFeignClients 注解上也有。
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
......
}
@EnableFeignClients
意思就是 “开启FeignClient功能”,其本质就是在Spring容器中注册一些Bean的定义,后续根据这些定义实例化这些Bean,这些Bean实例提供了FeignClient的功能。
我们列出了简略的代码如下:
Spring容器会顺着启动类AppBMain
(也是配置类),扫描到其 @Import 的类 FeignClientsRegistrar
,然后使用反射对其实例化,调用栈如下
addImportBeanDefinitionRegistrar:199, ConfigurationClass (o.s.context.annotation)
// ^将FeignClientsRegistrar实例放入容器中(一个配置文件对应一个)
processImports:604, ConfigurationClassParser (o.s.context.annotation)
// ^实例化@import注解导入的类FeignClientsRegistrar(@Import(FeignClientsRegistrar.class)),并将其放入容器
// 导入的类型分三种:1、ImportSelector(会递归处理);2、ImportBeanDefinitionRegistrar;3、其他(当做配置类来处理)
doProcessConfigurationClass:304, ConfigurationClassParser (o.s.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (o.s.context.annotation)
// ^处理解析出来的的配置类
parse:200, ConfigurationClassParser (o.s.context.annotation)
// ^为每个配置类创建一个ConfigurationClass实例
// 并放入ConfigurationClassParser实例的Map<ConfigurationClass, ConfigurationClass>类型的属性中
parse:169, ConfigurationClassParser (o.s.context.annotation)
// ^解析配置类
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (o.s.context.annotation)
// ^找出配置类
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (o.s.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (o.s.context.support)
refresh:525, AbstractApplicationContext (o.s.context.support)
refresh:122, EmbeddedWebApplicationContext (o.s.boot.context.embedded)
refresh:693, SpringApplication (o.s.boot)
refreshContext:360, SpringApplication (o.s.boot)
run:303, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:15, AppBMain (com.yh.stu)
从调用栈中processConfigBeanDefinitions:308
看出,调用栈中 processImports:604,ConfigurationClassParser
,就在这个方法中,实例化所有@import的类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
......
processImports(configClass, sourceClass, getImports(sourceClass), true);
......
}
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}
// 递归调用来查找@import注解标注的 SourceClass
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
//递归调用
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
......
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
......
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
......
}
}
......
}
调用其实现ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,在这个方法中注册Feign功能需要的bean的定义到容器中。
Spring Boot的启动类 AppBMain 为什么是配置类? 因为其标注的@SpringBootApplication 间接标注了 @Configuration,而 Spring是循环递归扫描的注解的,所以Spring 认为 AppBMain 也是个配置类。
下面是我debug时候的调用栈,大家看看注释里的解释
registerFeignClient:176, FeignClientsRegistrar (o.s.cloud.netflix.feign)
registerFeignClients:167, FeignClientsRegistrar (o.s.cloud.netflix.feign)
registerBeanDefinitions:92, FeignClientsRegistrar (o.s.cloud.netflix.feign)
// 调用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,这里通常就是用来注册bean的定义的
loadBeanDefinitionsFromRegistrars:360, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// 从容器中 ImportBeanDefinitionRegistrar实例中加载bean定义
loadBeanDefinitionsForConfigurationClass:144, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// ^加载配置文件中的bean定义,注:可以将ImportBeanDefinitionRegistrar的实例看做bean的定义,实例里本来就是用来定义了一些bean
loadBeanDefinitions:116, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// 使用ConfigurationClassBeanDefinitionReader 来解析配置并注册bean定义
processConfigBeanDefinitions:320, ConfigurationClassPostProcessor (o.s.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (o.s.context.annotation)
// ConfigurationClassPostProcessor 循环递归解析配置并注册bean定义
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (o.s.context.support)
// 调用容器中的所有的BeanDefinitionRegistryPostProcessor
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (o.s.context.support)
refresh:525, AbstractApplicationContext (o.s.context.support)
refresh:122, EmbeddedWebApplicationContext (o.s.boot.context.embedded)
refresh:693, SpringApplication (o.s.boot)
refreshContext:360, SpringApplication (o.s.boot)
run:303, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:15, AppBMain (com.yh.stu)
2、FeignClientsRegistrar注册FeignClientFactoryBean
FeignClientsRegistrar类实现了一些接口(代码如下),这些接口的作用属于Spring Framework的知识。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
.......
}
ℹ️ Spring Framwork 知识-
ImportBeanDefinitionRegistrar
接口
在容器启动过程的中,会调用后置处理器阶段会调用容器中所有类型为ImportBeanDefinitionRegistrar
类型的实例的registerBeanDefinitions(...)
方法,该方法向容器中注册Bean 的定义,这些Bean的定义在Bean的实例化时需要使用。(解析出配置的所有配置类中配置的Bean的定义,包括@Import标注的类中定义的Bean)。
在refresh的 invokeBeanFactoryPostProcessors(beanFactory) 方法中,会调用“配置类后置处理器”(ConfigurationClassPostProcessor)来解析Spring应用的配置。这个“配置类后置处理器”是Spring Framework 容器创建的阶段就注册进容器了(不是refresh阶段)。
ImportBeanDefinitionRegistrar
类型的Bean就是在这个阶段实例化并调用的。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// metadata就是主启动类上的注解元数据
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);//添加FeignClient注解的过滤
basePackages = getBasePackages(metadata);
}else {
......
}
for (String basePackage : basePackages) {
//查找包下所有标注@FeignClient的类
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();
.......
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
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("name", name);// app-a
definition.addPropertyValue("type", className);
.......
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
......
3、调用FeignClientFactoryBean的getObject
FeignClientsRegistrar注册了FeignClientFactoryBean的BD后,
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
// 如果你在@FeignClient上,没有配置url属性,也就是你没有自己指定服务的url地址,那么就会自动跟ribbon关联起来,
// 采用ribbon来进行负载均衡,直接就开始为ribbon来准备对应的url地址了
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
protected Feign.Builder feign(FeignContext context) {
// 这里为app-a 创建了一个ApplicationContext
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
configureFeign(context, builder);
return builder;
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include
spring-cloud-starter-netflix-ribbon?");
}
1、applicationContext.getBean(FeignContext.class) 调用栈:
<init>:31, FeignContext (o.s.cloud.netflix.feign)
feignContext:72, FeignAutoConfiguration (o.s.cloud.netflix.feign)
CGLIB$feignContext$1:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
(o.s.cloud.netflix.feign)
invoke:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
$$FastClassBySpringCGLIB$$55cd8dc9 (o.s.cloud.netflix.feign)invokeSuper:228, MethodProxy (o.s.cglib.proxy)
intercept:358, ConfigurationClassEnhancer$BeanMethodInterceptor (o.s.context.annotation)
feignContext:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129 (o.s.cloud.netflix.feign)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
instantiate:162, SimpleInstantiationStrategy (o.s.beans.factory.support)
// ^ 反射调用return factoryMethod.invoke(factoryBean, args);这里的factoryBean是CGLIB代理类:
// FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
instantiateUsingFactoryMethod:588, ConstructorResolver (o.s.beans.factory.support)
instantiateUsingFactoryMethod:1181, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
createBeanInstance:1075, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
doCreateBean:513, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
createBean:483, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
getObject:312, AbstractBeanFactory$1 (o.s.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (o.s.beans.factory.support)
doGetBean:308, AbstractBeanFactory (o.s.beans.factory.support)
getBean:220, AbstractBeanFactory (o.s.beans.factory.support)
resolveNamedBean:1018, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:345, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:340, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:1092, AbstractApplicationContext (o.s.context.support)
getObject:220, FeignClientFactoryBean (o.s.cloud.netflix.feign)
----------------------
略......