决战圣地玛丽乔亚Day40---Spring容器的启动流程


Spring启动过程?

tips:

 BeanDefinition的定义:

 Spring是Bean的容器,Bean和普通的java实例的区别就在于bean在java的实例上加入了一些Spring封装的属性(作用域,加载模式,是否单例等...)

 BeanDefinition就是用来实例化对应的bean。

BeanDefinitionReader定义:

从配置文件读取Bean解析成BeanDefinition

 BeanDefinitionRegistry:

 存储bean解析后的BeanDefinition

BeanFacotry:

根据BeanDefinition将Bean生产出来并保存。

BeanPostProcessor :

对Bean的生命周期进行干预

BeanFactoryPostProcessor:

对BeanFactory进行干预

在聊启动流程之前,放几张图:

 

 

 

 

 

 

 

1.初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中

  如果是注解的方式,会调用AnnotationConfigApplicationContext 

  如果是使用xml方式,会调用ClassPathXmlApplicationContext 

我们以AnnotationConfigApplicationContext 为例进行分析。

 

 

1. this():

 

 

 调用自身构造器,主要是实例化两个对象,一个是

AnnotatedBeanDefinitionReader

 

 

 INSTANCE:

 

 

    所以这里的INSTANCE是一个空的。这里是在设置默认的Bean名称生成器

然后new 一个 AnnotationScopeMetadataResolver

 

 

 这里的

ScopedProxyMode.NO代表不创建代理对象
ScopedProxyMode有三种类型:NO,INTERFACES,TARGET_CLASS.
NO是不创建代理对象,应用Bean的作用域,例如是多例则不需要创建代理对象。
INTERFACES使用JDK动态代理生成代理对象。
TARGET_CLASS使用CGLIB创建代理对象。

Bean的代理主要是由于作用于不同会产生线程安全问题,如果单例模式的Bean注入到多例的作用域可能会有污染,就可以使用代理模式来生成对应的bean。

紧接着是两个断言,进行非空判断。

 

 

next,初始化BeanDefinitionRegistry、Environment和ConditionEvaluator

 

 

 这里是注解形式,所以ResouceLoader是null即可。ConditionEvaluator用于根据条件工具,可以根据不同的Condition注解对bean加载的时机等进行判断。

最重要一行代码:

 

 

 

 

 

 registerAnnotationConfigProcessors的作用是注册一些注解处理器,以便能够处理不同的注解,并将其转化为对应的BeanDefinition对象,并将其注册到容器中,方便后续使用。

 初始化的过程可以看到初始化beanFactory为DefaultListableBeanFactory

并且通过unwrapDefaultListableBeanFactory方法,从参数(BeanDefinitionRegistry)registry中获取DefaultListableBeanFactory对象。

 然后为bean设置DependencyComparator(用来比较两个bean的依赖关系)和AutowireCandidateResolver(用于确定哪些Bean对象可以被自动注入到其他Bean对象中)

这两个方法是DefaultListableBeanFactory实现的,这也是为什么要使用DefaultListableBeanFactory的原因。

调用setDependencyComparator和setAutowireCandidateResolver等方法是为了设置BeanFactory的扩展点,以便在后续的Bean加载和依赖注入过程中,能够更好地控制Bean之间的依赖关系和注入行为。这些方法是BeanFactory接口的扩展点,只有DefaultListableBeanFactory实现了这些方法。因此,如果使用其他的BeanFactory对象替代DefaultListableBeanFactory,则可能无法使用这些扩展点,从而无法更好地控制Bean之间的依赖关系和注入行为,可能会导致程序的正确性和稳定性问题。

介绍完了AnnotatedBeanDefinitionReader,别忘了还有一个ClassPathBeanDefinitionScanner

 

 

 

 

 

ClassPathBeanDefinitionScanner这些操作都是为了在后续的扫描过程中能够更加准确地定位和注册BeanDefinition

this()总结:  主要初始化一些注册和生成BeanDefinition的工具和处理器。以便于把bean封装成BeanDefinition并注册。

 

 

2.this.register(conponentClasses)注册

 

 

 

 1)

 

 setBeanClass不用说,是设置AnnotatedGenericBeanDefinition对象对应的beanclass。

下面的metadata是从bean对象中获取的各种注解元数据。包括注解类型、注解属性、注解中定义的方法等

 通过AnnotatedGenericBeanDefinition,可以把传入的bean作为BeanDefination的bean。

2)

 

 判断注解是否满足可以被加载的条件,如果满足就执行if里面的语句。

 abd.setInstanceSupplier(supplier) 提供bean实例的创建逻辑,会在bean实例化的时候调用。

 

 

 它会根据Bean类上的@Scope注解来确定Bean的作用域,并返回一个ScopeMetadata对象,该对象包含了作用域名称、是否是单例等信息。然后将该作用域名称设置到BeanDefinition对象中。

 

 

 生成bean的名称,如果没有指定名称则由generateBeanName生成一个唯一名称

 

 

这里的AnnotationConfigUtils是一个工具类,它会处理一些通用的定义注解,例如@Lazy、@Primary等。

具体来说,它会根据Bean类上的这些注解来设置BeanDefinition对象的相应属性,例如设置是否延迟初始化、是否为主要候选Bean等。

 

 

 这里的qualifiers是一个Class类型的数组,用于指定自动装配的限定符。

具体来说,它会遍历数组中的每个限定符,根据限定符的类型设置BeanDefinition对象的相应属性,例如设置是否为主要候选Bean、是否延迟初始化等。

如果限定符不是@Primary或@Lazy,则会将其添加到BeanDefinition对象的限定符列表中,该列表用于在自动装配时进行匹配(懒匹配)

 

 

 在这里,实现的是自定义修改BeanDefinition属性。

 

 

BeanDefinitionHolder就是一个BeanDefinition的持有者,这里就是简单的把BeanDefination封装为BeanDefinitionHolder

 

 

 

 它会根据Bean定义的作用域以及@Bean注解的proxyMode属性,决定是否需要为该Bean创建作用域代理。

具体来说,它会根据传入的scopeMetadata和definitionHolder对象来判断是否需要创建作用域代理,并将新的Bean定义封装成DefinitionHolder对象返回。

在这个过程中,如果需要创建作用域代理,则AnnotationConfigUtils会调用ScopedProxyFactoryBean来创建代理对象,并将代理对象的Bean定义封装成DefinitionHolder对象返回。

最终,DefinitionHolder对象会被注册到BeanDefinitionRegistry中,成为一个正式的Bean定义。

 

 

 下面是registerBeanDefinition方法在DefaultListableBeanFatory中的实现:

 就是把BeanDefinition注册到spring中,其实就是找个Map容器存储起来,后面若要使用则直接从这个Map中获取。

总体来说,首先对BeanDefiniton  的校验,具体来说,是对AbstractBeanDefinition属性中的methodOverrides 效验; 效验methodOverrides 是否与工厂方法并存或者methodOverrides对应的方法根本不存在。

然后对容器中已经存在了一个同名bean的处理方法。如果对应beanName已经注册 并且beanName不能被覆盖,则抛出异常,否则后注册的覆盖先注册的。

正常情况会将beanName和beanDefinition放到beanDefinitionMap中,此时的beanDefinitionMap中已经存在了5个beanDefinition,前面一节调用无参构造 this()中放进去的5个处理器。

 registerBeanDefinition方法之后,还有registry.registerAlias(beanName, alias)方法,这是给bean注册别名用的。其实就是spring上线文对bean名称和别名之间维护了映射关系。

到此注册结束。

register()总结:根据注解判断是否满足加载条件,bean封装到BeanDefinition,并且额外的信息也要封装到BeanDefinition(懒加载,作用域代理,是否主要等等属性),最后把BeanDefinition根据一定规则注册到Spring中。

 

posted @   NobodyHero  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示