1.【Spring源码】- 02 Spring IoC容器启动之refresh方法
2.Spring IoC源码深度剖析
3.Spring中依赖注入的码讲四种方式(spring依赖注入有哪些实现方式)
4.Spring IoC:getBean 详解
5.SpringIoc 容器之 Aware | 京东云技术团队
【Spring源码】- 02 Spring IoC容器启动之refresh方法
在注册阶段,AnnotationConfigApplicationContext构造方法中的码讲第一个方法被分析过。接下来,码讲我们关注第二个方法:register(componentClasses)。码讲在使用XML配置方式时,码讲通过new ClassPathXmlApplicationContext("classpath:spring.xml")来创建实例,码讲游戏组件源码其中需要指定xml配置文件路径。码讲使用注解方式时,码讲也需要为ApplicationContext提供起始配置源头,码讲这里使用配置类代替xml配置文件,码讲按照配置类中的码讲注解(如@ComponentScan、@Import、码讲@Bean)解析并注入Bean到IoC容器。码讲
通过配置类,码讲Spring解析注解实现Bean的码讲注入。使用@Configuration注解定义的配置类相当于xml配置文件,但目前Spring推荐使用注解方式,xml配置的使用概率正在降低。
register(componentClasses)方法的核心逻辑在AnnotatedBeanDefinitionReader#doRegisterBean中,将传入的配置类解析为BeanDefinition并注册到IoC容器。ConfigurationClassPostProcessor这个BeanFactory后置处理器在IoC初始化时,获取配置类的BeanDefinition集合,开始解析。
真正启动IoC容器的流程在refresh()方法中,这是了解IoC容器启动流程的关键步骤。refresh方法在AbstractApplicationContext中定义,采用模板模式,提供IoC初始化流程的基本实现,子类可以扩展。
下面分析refresh()方法的每个步骤,以了解IoC容器的启动流程。
prepareRefresh方法主要在refresh执行前进行准备工作,如设置Context的启动时间、状态,以及扩展系统属性相关。
initPropertySources()方法主要用于扩展配置来源,openjpg源码分析如网络、物理文件、数据库等加载配置信息。StandardEnvironment默认只提供加载系统变量和应用变量的功能,用于子类扩展。
❝initPropertySources方法常见扩展场景包括:❞
getEnvironment().validateRequiredProperties()确保设置的必要属性在环境中存在,否则抛出异常终止应用。
BeanFactory是Spring的基本IoC容器,ApplicationContext包装了BeanFactory,提供更智能、更便捷的功能。ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();获取的BeanFactory是IoC容器初始化工作的基础。
上面获取的BeanFactory还不能直接使用,需要填充必要的配置信息。至此,IoC容器的启动流程基本完成。
这里对IoC启动流程有个大致、直观的印象。主要步骤包括:准备阶段、配置来源扩展、初始化BeanFactory、填充配置、解析配置类、注册Bean、实例化BeanPostProcessor、初始化国际化和事件机制、以及创建内嵌Servlet容器(在SpringBoot中实现)。这些步骤确保了IoC容器顺利启动并管理Bean。
Spring IoC源码深度剖析
Spring IoC容器初始化深度剖析
Spring IoC容器是Spring的核心组件,主要负责对象管理和依赖关系管理。容器体系丰富多样,如BeanFactory作为顶层容器,它定义了所有IoC容器的基本原则,而ApplicationContext及其子类如ClassPathXmlApplicationContext和AnnotationConfigApplicationContext则提供了额外功能。Spring IoC容器的网页合成源码初始化流程关键在AbstractApplicationContext的refresh方法中。 1.1 初始化关键点 通过创建特定类LagouBean并设置断点,我们发现Bean的创建在未设置延迟加载时,发生在容器初始化过程中。构造函数调用、InitializingBean的afterPropertiesSet方法以及BeanFactoryPostProcessor和BeanPostProcessor的初始化和调用,都在refresh方法的不同步骤中发生。 1.2 主体流程概览 Spring IoC容器初始化的主体流程主要集中在AbstractApplicationContext的refresh方法,涉及Bean对象创建、构造函数调用、初始化方法执行和处理器调用等步骤。 1.3 深度剖析 分析发现,延迟加载机制使得懒加载的bean在第一次调用getBean时才进行初始化。而对于非懒加载bean,它们在容器初始化阶段已经完成并缓存。Spring处理循环依赖的方法依赖于构造器调用的顺序规则,不支持原型bean的循环依赖,而对单例bean则通过setXxx或@Autowired方法提前暴露对象来避免循环依赖。Spring中依赖注入的四种方式(spring依赖注入有哪些实现方式)
spring依赖注入通常有哪些实现方式,列举并分别进行说明。
3种方法缺冲
1。构造器注入
publicclassxx{
privateManagermanage;
publicxx(Managermanage){
this.manage=manage;
}
}
2.setter方法注则迅入
publicclassxx{
privateManagermanage;
publicvoidsetManager(Managermanage){
this.manage=manage;
}
}
3.接口注伏盯歼入
publicinterfaceManager{
publicvoidmanage(Businessbusiness);
}
publicclassxx{
privateBusinessbusiness;
publicvoidmanage(Businessbusiness){
this.business=business;
}
}
Spring依赖注入是java自带的注解,有两个属性name?和?type,Spring支持使用@Resource来注入。
1、加到类的属性字段上,默认会使用反射机制来友链实现注入;
2、原则上要加到类的属性set方法上。
1、加到类的属性字段上,默认的name是属性名称,默认的type是属性类型
2、加到类的属性set方法上,默认的name是方法名去掉set后将其首字母小写?或?方法的参数名称(2个有一个存在即可),默认的type就是set方法的参数类型。
1、都不显示指定时,源码防止拷贝默认先按照byName,再按照byType来查找类并注入
2、显示指定了name,则只会按照byName查找类并注入
3、显示指定了type,则只会按照byType查找类并注入
4、都指定时,则查找同时满足byName和byType的类并注入
byName的原则,查找beanId等于指定的名称的bean,找不到则抛出异常。
byType的原则,查找指定的类、实现类或子类,找不到或是找到多个,都会抛出异常。
是spring提供的注解,有一个属性required,表示注入时如果bean不存在是否允许。
(默认是true表示不允许为空,否则报错。)
1、加到类的属性字段上,默认会使用反射老纤机制来实现注入;
2、原则上要加到类的属性set方法上。
1、加到类的好含孙属性字段上,默认的name是属性名称,默认的type是属性类型
2、加到类的属性set方法上,默认的name是方法的参数名称,默认的type就是set方法的参数类型。
默认先按照byType查找类并注入,再按照byName查找类并注入。
byName的原则,查找beanId等于指定的大武林源码名称的bean,找不到则抛出异常。
byType的原则,查找指定的类、实现类或子类,找不到或是找到多个,都会抛出异常。
spring依赖注入有几种方式注入就有这三种方法
接口注入(不推荐)
getter,改则setter方式注入(比较常用)这个就是依赖注搏祥入
构造器注入(死的应基歼搏用)
写出spring基于xml注入的几种方式?
1.构造器注入:通过在类的构造器中传入依赖对象来完成依赖注入。
2.Setter方法注入:通过在类中定锋轿义setter方法来完成依赖注入。
3.接口注入:通过在类中定义庆裂接口来完成依赖注入,依赖对象实现该接誉基闭口并在类中调用该接口方法。
SpringIOC的四种注入方式控制反转(InversionofControl),是一种设计思想,而依赖注入(DI)是一种实现的方法。原本对象的创建是依靠程序员来创建,通过依赖注入的方法来改造后,对象的创建是依赖IOC容器,对象的属性依赖IOC容器注入。
setter是Spring现在最主流的注入方式,它可以利用JavaBean规范所定义set/get方法来完成注入,可读性灵活性高,它不需要使用构造器注入时出现的多个参数,它可以把构造方法声明成无参构造,再使用setter注入设置相对应的值,其实也是通过java反射技术去实现的。
xml文件配置:
构烂镇造器注握凯入主要是依赖于段历唤构造方法去实现,构造方法可以是有参也可以是无参,我们在平常都是通过类的构造方法来创建类对象,以及给他赋值,同样Spring也可以采用反射的方式,通过构造方法来完成注入(赋值)。
xml文件配置:
@Autowired默认按类型装配
@Qualifier和Autowired配合使用,指定bean的名称
@Resource默认按名称装配,当找不到与名称匹配的bean时,才会按类型装配。
接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在IOC的专题世界内并不被看好。
Spring IoC:getBean 详解
接着 Spring IoC:finishBeanFactoryInitialization 详解,我们正式开始学习获取 bean 实例方法,该方法是 Spring 最核心的方法。
单击 preInstantiateSingletons 方法里的 getBean(beanName) 代码,进入该方法。
见 doGetBean 方法详解。
doGetBean
1.解析 beanName,主要是解析别名、去掉 FactoryBean 的修饰符 “&”,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块4已解析过。
2.尝试从缓存中获取 beanName 对应的实例,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块7已解析过。
3.1 返回 beanName 对应的实例对象(主要用于 FactoryBean 的特殊处理,普通 bean 会直接返回 sharedInstance 本身),见代码块1详解。
6.如果不是仅仅做类型检测,而是创建 bean 实例,这里要将 beanName 放到 alreadyCreated 缓存,见代码块5详解。
7.根据 beanName 重新获取 MergedBeanDefinition,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块2已解析过。
8.2 检查 dep 是否依赖于 beanName,即检查是否存在循环依赖,见代码块6详解。
8.4 将 dep 和 beanName 的依赖关系注册到缓存中,见代码块7详解。
9.1 scope 为 singleton 的 bean 创建(新建了一个 ObjectFactory,并且重写了 getObject 方法),见代码块8详解。
9.1.1、9.2.2、9.3.4 创建 bean 实例,限于篇幅,在下篇文章单独解析。
9.1.2、9.2.4、9.3.6 返回 beanName 对应的实例对象,见代码块1详解。
9.2.1 scope 为 prototype 时创建实例前的操作、9.2.3 scope 为 prototype 时 创建实例后的操作,相对应的两个方法,见代码块详解。
代码块1:getObjectForBeanInstance
如果对 FactoryBean 不熟悉的,可以回头去看 Spring IoC:finishBeanFactoryInitialization 详解 中对 FactoryBean 的简单介绍。
6.mbd 为空,但是该 bean 的 BeanDefinition 在缓存中存在,则获取该 bean 的 MergedBeanDefinition,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块2已经解析过。
8.从 FactoryBean 获取对象实例,见代码块2详解。
代码块2:getObjectFromFactoryBean
3.调用 FactoryBean 的 getObject 方法获取对象实例,见代码块3详解。
5.对 bean 实例进行后续处理,执行所有已注册的 BeanPostProcessor 的 postProcessAfterInitialization 方法,见代码块4详解。
代码块3:doGetObjectFromFactoryBean
很简单的方法,就是直接调用 FactoryBean 的 getObject 方法来获取到对象实例。
细心的同学可以发现,该方法是以 do 开头,看过 Spring IoC:源码总览 的同学知道,我在总览里就特别提到以 do 开头的方法是最终进行实际操作的方法,例如本方法就是 FactoryBean 最终实际进行创建 bean 对象实例的方法。
代码块4:postProcessObjectFromFactoryBean
这边走的是 AbstractAutowireCapableBeanFactory 里的方法。通过前面的介绍,我们知道创建的 BeanFactory 为 DefaultListableBeanFactory,而 DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory,因此这边会走 AbstractAutowireCapableBeanFactory 的重写方法。
在 Spring IoC:registerBeanPostProcessors 详解 中已经学过 BeanPostProcessor,在创建完 bean 实例后,会执行 BeanPostProcessor 的 postProcessAfterInitialization 方法。
代码块5:markBeanAsCreated
2.这边会将 beanName 对应的 MergedBeanDefinition 移除,然后在之后的代码重新获取,主要是为了使用最新的 MergedBeanDefinition 来进行创建操作。
代码块6:isDependent
这边引入了一个缓存 dependentBeanMap:beanName -> 所有依赖 beanName 对应的 bean 的 beanName 集合。内容比较简单,就是检查依赖 beanName 的集合中是否包含 dependentBeanName,隔层依赖也算。例如:A 依赖了 B,B 依赖了 C,则 A 也算依赖了 C。
代码块7:registerDependentBean
这边又引入了一个跟 dependentBeanMap 类似的缓存,dependenciesForBeanMap:beanName -> beanName 对应的 bean 依赖的所有 bean 的 beanName 集合。
这两个缓存很容易搞混,举个简单例子:例如 B 依赖了 A,则 dependentBeanMap 缓存中应该存放一对映射:其中 key 为 A,value 为含有 B 的 Set;而 dependenciesForBeanMap 缓存中也应该存放一对映射:其中 key 为:B,value 为含有 A 的 Set。
代码块8:getSingleton
5.创建单例前的操作,7.创建单例后的操作,这两个方法是对应的,见代码块9详解。
6.执行 singletonFactory 的 getObject 方法获取 bean 实例,该方法会走文章开头 doGetBean 方法的注释 9.1.1。
8.如果是新的单例对象,将 beanName 和对应的单例对象添加到缓存中,见代码块详解。
代码块9:beforeSingletonCreation、afterSingletonCreation
inCreationCheckExclusions 是要在创建检查排除掉的 beanName 集合,正常为空,可以不管。这边主要是引入了 singletonsCurrentlyInCreation 缓存:当前正在创建的 bean 的 beanName 集合。在 beforeSingletonCreation 方法中,通过添加 beanName 到该缓存,可以预防出现构造器循环依赖的情况。
为什么无法解决构造器循环依赖?
我们之前在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块7提过,getSingleton 方法是解决循环引用的核心代码。解决逻辑的第一句话:“我们先用构造函数创建一个 “不完整” 的 bean 实例”,从这句话可以看出,构造器循环依赖是无法解决的,因为当构造器出现循环依赖,我们连 “不完整” 的 bean 实例都构建不出来。Spring 能解决的循环依赖有:通过 setter 注入的循环依赖、通过属性注入的循环依赖。
代码块:addSingleton
代码块:beforePrototypeCreation、afterPrototypeCreation
该方法和代码块9的两个方法类似。主要是在进行 bean 实例的创建前,将 beanName 添加到 prototypesCurrentlyInCreation 缓存;bean 实例创建后,将 beanName 从 prototypesCurrentlyInCreation 缓存中移除。这边 prototypesCurrentlyInCreation 存放的类型为 Object,在只有一个 beanName 的时候,直接存该 beanName,也就是 String 类型;当有多个 beanName 时,转成 Set 来存放。
总结
本文介绍了获取 bean 实例的大部分内容,包括先从缓存中检查、 FactoryBean 的 bean 创建、实例化自己的依赖(depend-on 属性)、创建 bean 实例的前后一些标记等,在下篇文章中,将解析创建 bean 的内容。
推荐阅读
SpringIoc 容器之 Aware | 京东云技术团队
Aware 是 Spring 提供的一个标记超接口,它允许 bean 通过回调式方法接受 Spring 容器的通知,进而获取到容器中特定对象的实例。方法的具体实现由各个子接口决定,通常包含一个接收单个参数并返回 void 的方法。
Spring 中包含了 9 个内置的 Aware 实现,这些实现大致分为两类。前三个实现通过直接调用完成,而后六个则通过 ApplicationContextAwareProcessor 后置处理器间接回调。
实现 BeanNameAware 接口的 bean 需要实现 setBeanName() 方法。此接口的主要作用在于让实现该接口的 bean 知道自己在 Spring 容器中的名称。官方建议实际开发中不建议使用此接口,因为 bean 名称在 Spring 容器中可能仅作为唯一标识,与 bean 的联系并不紧密。
BeanClassLoaderAware 接口在 bean 属性填充后初始化前提供类加载器的回调,用于让受管理的 bean 了解其是由哪个类加载器负责装载的。
BeanFactoryAware 接口在 bean 属性填充后初始化前提供 bean 工厂的回调,实现该接口的 bean 可以直接访问 Spring 容器,根据传入参数动态获取被 Spring 工厂加载的 bean。
EnvironmentAware 接口让所有注册到 Spring 容器内的 bean,在启动时能够获取 application.properties 配置文件的属性值,避免将魔法值写入代码中。
EmbeddedValueResolverAware 接口用于基于 Spring 解析 properties 文件属性值,适用于抽象类中基于 Spring 解析 @Value 的方式。
ResourceLoaderAware 接口希望拥有 ResourceLoader 引用的对象,允许实现该接口的 bean 在被 Spring 管理时,通过 application context 载入资源,但通常建议使用特定的满足所有需求的 ResourceLoader 实现。
ApplicationEventPublisherAware 接口提供了一个用于为 Service 注入 ApplicationEventPublisher 事件发布器的接口,使 Service 可以发布事件。
MessageSourceAware 接口让 bean 获得 message source,用于在国际化场景中获取文本信息。
ApplicationContextAware 接口允许 bean 方便地获取 Spring 容器ApplicationContext,从而获取容器内的 Bean。Spring 在创建实现类时自动执行此接口的方法,将 ApplicationContext 注入。
Spring 中的 Aware 接口在 AbstractAutowireCapableBeanFactory.initializeBean() 方法中,通过调用 invokeAwareMethods() 和 applyBeanPostProcessorsBeforeInitialization() 方法触发 Aware 方法的调用。
invokeAwareMethods() 方法直接回调,而 applyBeanPostProcessorsBeforeInitialization() 方法通过 ApplicationContextAwareProcessor.postProcessBeforeInitialization() 间接调用,并在方法 invokeAwareInterfaces() 中进行回调。
总结而言,Spring 使用两种方式调用 Aware 接口,旨在在 Spring 容器生命周期中提供灵活的回调机制,允许 bean 在初始化前获取不同上下文信息。通过理解这 9 种内置实现,开发者可以更好地利用 Spring 提供的灵活性和功能。
2024-12-23 01:01
2024-12-23 00:50
2024-12-23 00:23
2024-12-23 00:16
2024-12-23 00:11
2024-12-22 23:07
2024-12-22 23:05
2024-12-22 23:01