spring容器启动创建javaBean步骤

下面三张图是我创建feign服务的方法,以feign为例来看javaBean的创建

 

 结论:spring容器启动时有个refresh方法,会将工程目录所有要生成 javaBean的类文件都加载存进一个BeandefinitionMap中,键值对形式,Key是beanName, value是Bean描述文件,然后根据描述文件进行javaBean实例化。

 

由于定位feign对象的fallbackFactory属性是如何注入进javabean对象的,调试代码顺便记录下spring容器启动

上图是获得feignJAVABEAN对象描述的方法执行顺序

到这里就是FeignClientsRegistrar类调用注册方法设置feign对象的属性(获取注解的属性值并注入生成描述文件)

 spring容器启动后

 第一步调用

org.springframework.boot.SpringApplication对象调用public ConfigurableApplicationContext run(String... args)方法做初始化
主要关注如下几个方法

1.prepareContext() 做工程应用的初始信息加载

2.refreshContext() (下图中的resfresh()) 扫描工程下javabean,并生成javabean实例

     2.1invokeBeanFactoryPostProcessors()会扫描工程下class文件信息用作初始beanIndifinition,将javabeanName和javaBean文件描述信息建立对应关系

     2.2finishBeanFactoryInitialization() 创建javaBean实例(见下图,代码暂时不看后面再描述)

3.afterRefresh(); 是spring框架留给我们自定义的接口方法

 

 

invokeBeanFactoryPostProcessors则会开始扫描工程下的组件

  org.springframework.context.annotation.ConfigurationClassPostProcessor类调用processConfigBeanDefinitions()方法会扫描javaBean并将信息存进

 看上图扫描出来118个组件,但是只有5个是有 beanName的,其他的beanName都是null,则就需要使用louadBeanDefinitions()方法去加载类文件信息获得javaBean名称

这里主要说两个方法 一个是1.parse方法(扫描组件) 2.loadBeanDefinitions方法(将javabean文件放进beanDefinition 如果是需要registrar注册器注册的javaBean 也会在这个方法内去扫描拿到信息加载进beanDefinition)

简单说:1.parse方法只能扫描到spring组件(@component, @Controller,@Service之类) 像feign这种三方件就需要通过注册器去扫描加载,换而言之如果你自定义一个组件希望spring容器管理,也就需要实现注册器等接口 这样spring容器

启动代码才能加载自定义的组件信息并让spring容器管理。

见下图 初始解析后扫描出来的组件是118个 但是经过loadBeanDefinitions方法使用注册器二次扫描后组件是305个 并保存在registry中(开头的feign服务接口javaBeanName和描述文件也在其中)

 

1.parse方法扫描组件

org.springframework.context.annotation.ComponentScanAnnotationParser.public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) 方法解析要扫描的包
该方法会通过@springbootapplication配置的basepackage basepackageclasses属性去取要扫描的包路径 如果都没有则取该@springbootapplication注解标记的类所在包为扫描路径
并且new一个ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner类来进行扫描
最后一行代码是scanner

 doscan()方法见下图将扫描的javaBean及说明 存进beanDefinitions (看右边储存的信息 都是class文件路径以及说明信息 并不是javaBean对象)

 

 2.this.reader.loadBeanDefinitions(configClasses);该方法依次遍历 parse解析出来的javabean文件说明来给javaBean命名

同时判断这些javabean.class文件是否有importBeanDefinitionRegistrars(注册器,如果有注册器还需要根据注册器二次扫描javaBean)

这也是前面图中第一次扫描只有118个组件,二次扫描有305个组件的原因。

 下图是loadBeanDefinitions方法的方法体,

 

调用

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader类执行loadBeanDefinitionsForConfigurationClass()

FeignclientService1Application这个启动类有引入两个注册器用于加载三方件。见下图

 

重点关注这两个方法(见上图)

this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());  //读取配置文件来初始化javaBean说明文件
this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); //调用registra(类提供的注册器)来初始化javaBean 说明文件
上面的feign对象初始化就需要注册器来注册

 两个注册器 AutoConfigurationPackages FeignClientsRegistrar

(也就是说要使用spring容器通过注册器初始化 javaBean 则必须在提供的jar包中实现importBeanDefinitionRegistrar接口,这样spring容器启动时也就可以调用该接口的方法进行bean说明文件初始化,实现该接口既可以自定义初始化)

 

 注册实现类重载了注册方法(见下图),调用了两个方法

registerDefaultConfiguration( )通过xml配置文件初始化javabean

registerFeignClients()通过@FeignClient注解来初始化javaBean说明文件 (重点看该方法)

 

 

将代码registerFeignClients()方法拷出来方便写注释

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//由于可能配置有多个@FeignClient注解的javaBean 因此创建集合来存储
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
//加载EnableFeignClients的注解属性 共有五个value basePackages basePackageClasses defaultConfiguration clients
//@EnableFeignClients是配置在启动类上的注解
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
  //clients就是你在启动类上通过注解配置的要创建的feign服务 通过注解配置获取后放入集合
if (clients != null && clients.length != 0) {
Class[] var12 = clients;
int var14 = clients.length;

for(int var16 = 0; var16 < var14; ++var16) {
Class<?> clazz = var12[var16];
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
} else {
    //通过扫描器去扫描带有@FeignClient注解的类 然后放入集合 见下图
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
     //通过注解@EnableFeignClients去获取属性basePackage和basePackageClassses的值,并将这些值作为要扫描的包路径集合
// 如果包路径集合为空 则取@EnableFeignClients标记的类所在包为扫描路径
//(类似@mapperScan如果没有配置,则取@enableAutoConfiguration(该注解属于@SpringBootApplication)标记的类所在包为扫描路径)
        Set<String> basePackages = this.getBasePackages(metadata);
Iterator var8 = basePackages.iterator();

while(var8.hasNext()) {
String basePackage = (String)var8.next();
        //扫描器根据扫描包路径添加feignClient组件
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}

Iterator var13 = candidateComponents.iterator();

while(var13.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var13.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
//通过注解拿到该javabean的注解属性 以便初始化
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
        //这里有个坑 如果你配置@FeignClient注解 配置了contextId name value serviceId 那么优先级从低到高为serviceId <name <value<contextId 这个值最终是要消费的服务名
            String name = this.getClientName(attributes);
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}

}

扫描获得FeignController成功 然后调用registerFeignClient()方法初始化

通过获取注解中配置的fallbackFactory属性并注入javaBean

 

 上图方法没截完 接下图

 执行完断点方法后将@feignClient注解标识的类作为javaBeanName和javaBean说明文件存入BeanDefinitionMap和BeanDefinitionNames.

 下图是上图断点方法内部实现 将@feignClient注解标识的类作为javaBeanName和javaBean说明文件存入BeanDefinitionMap和BeanDefinitionNames.

 

参考如下两文(https://cloud.tencent.com/developer/article/1449288)

(https://blog.csdn.net/weixin_45481821/article/details/129703973)

 

posted on 2024-06-14 07:12  丶柚子  阅读(1)  评论(0编辑  收藏  举报

导航