Bean容器
- BeanFactory: The root interface for accessing a Spring bean container.
特点: 延迟初始化 (只有客户端访问BF管理的对象时,才对该对象进行初始化操作和DI)
使用场景: 容器启动较快,所需资源比较少 - ApplicationContext : 继承BF
特点: 更加完善,提供了其他的高级特性:事件发布,国际化信息支持等
使用场景: 系统资源充足,功能需求更多的场景中(目前使用较多)
SpringIOC
在 Spring 框架中,IOC(Inversion of Control,控制反转)是一种设计原则和编程范式,它通过将对象的创建和依赖关系的管理交给容器来实现,从而实现对象之间的解耦和灵活性。
在传统的编程模式中,对象之间的依赖关系通常是硬编码在代码中的,当一个对象需要使用另一个对象时,它必须直接创建或查找该对象。这样导致了对象之间紧密耦合,难以维护和扩展。
而在 Spring 中,IOC 容器扮演了一个重要的角色。它负责管理应用程序中的所有 Bean(对象),包括创建和销毁它们,并通过依赖注入(Dependency Injection,DI)的方式将对象之间的依赖关系自动注入到相应的 Bean 中。
具体来说,IOC 实现了以下几个核心概念:
-
Bean 定义:在 Spring 中,每个对象都被称为一个 Bean。Bean 定义描述了对象的创建和配置信息,通常在 XML
文件中进行配置。 -
IOC 容器:IOC 容器负责解析 Bean 的定义,并负责创建、管理和维护 Bean 的生命周期。Spring
提供了不同类型的 IOC 容器,最常见的是 ApplicationContext。 -
依赖注入(DI):IOC容器通过依赖注入的方式将对象之间的依赖关系注入到相应的 Bean 中。这样,在创建一个对象时,它所依赖的其他对象会自动被注入到它里面。
通过使用 IOC,开发者只需关注对象本身的实现,而无需关心对象之间的创建和管理过程,大大简化了代码的编写和维护。同时,IOC 也提高了代码的可测试性和可扩展性,因为对象之间的耦合度降低,可以轻松替换和调整 Bean 的配置。
Bean对象的生命周期
讲一下Bean的生命周期
spring是一个轻量级的框架,为了帮助我们进行简化开发,里面最核心的功能有IOC和AOP,Bean的生命周期只是IOC创建对象中的流程,大体上包括实例化,初始化,使用和销毁四个部分,这是粗粒度的一个描述。
那么从细粒度的角度上来说,先从实例化开始,通过反射的方式读取到BD中的相关信息,可以把对应的class对象创建出来,可以通过反射的方式加载我们的类并且创建出当前对象,此时的创建只是在堆空间中申请了一块内存空间,并没有实际的给对象进行复制,所以此时对象的属性是默认值的,当默认值结束后,会通过PopulateBean对我们的当前对象进行属性赋值操作,会调用对应的set方法完成属性的赋值,但是在这个步骤里面只是完成了自定义属性赋值的操作,还有一部分属性叫做容器属性,也就是再Spring框架中,他自己帮我们定义的一些类对象,我们自定义的对象中也可能会使用这些容器对象,所以对容器对象进行赋值的时候需要调用Aware接口,通过Aware接口的判断确定出是否能够进行属性值的设置工作,所以相当于PopulateBean和InvokeAwareMethods共同完成了对象的初始化工作。一个是自定义属性的赋值,一个是容器属性的赋值,当这个步骤执行成功之后,按照正常情况我们的Bean对象已经能够正常使用了,但是Spring是一个框架,他在进行设置时需要考虑一个特点:可扩展性,当bean对象创建完成后,可能有一些对Bean对象进行扩展的过程,比如说AOP的实现,其实就是对我们的Bean对象的一个扩展操作,因此在这个时候,会在BeanPostPocessor()对接口的after或者Before方法来进行一些相关的扩展操作,比如说AOP动态代理的实现就是在after方法里面实现的,而在Before和After方法中间,还有一个初始化方法的调用,这个初始化方法一般情况下是不进行使用的,但是这里面有一个非常重要的隐藏接口,叫initlizationBean,在这个接口里面,会给我们正在创建的对象留一个afterPropertySet方法,这个方法会给用户最后一次机会来完成一些属性和方法的调用,留给用户自己的空间,当这些步骤都执行完成之后,我们整个对象就已经创建结束了,那么现在我们就可以通过Context.GetBean方法来进行对象地使用了,当对象使用完毕后,在关闭容器的时候,会调用close方法,来把我们创建的对象进行销毁。
创建class对象的三种方式:1. 类.calss 2. Class.forName(类路径)3. 类名.getclass()
Spring创建对象和销毁对象的时机
循环依赖
Spring是如何解决的循环依赖
Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?
答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理
BeanFactory & FactoryBean
- BeanFactory
工厂,用于对象的创建,要经过一个完整的复杂的生命周期的处理过程
(Bean的生命周期,一个流水线的过程) - FactoryBean
Bean: 特殊的对象创建,假设这个对象只需要再某一个方法中用—次,使用完整的生命周期很麻烦,所以有了更加简单的方式
(私人定制)
- isSingleton: 判断当前对象是否是单例对象
- getObjectType:返回创建的对象的类型
- getObject:根据用户自定义的方式来选择对象的创建
- new
- 反射
- 动态代理
自动装配原理
讲一下自动装配的流程
自动装配简单来说就是自动把第三方jar 包中的Bean装配到IOC容器中,不需要开发人员再去写Bean相关的配置,在springboot框架的应用里面,只需要在启动类上加上@springbootApplication注解就可以实现自动装配,springbootApplication这样的注解是一个复合注解,真正去实现自动装配的注解是@EnableAutoConfiguration这样一个注解,自动装配的实现主要依靠三个核心的关键技术
- 引入Starter启动依赖组件的时候,组件必须包含一个@Configuration配置类,而在这个配置类里面我们需要通过@Bean这个注解去声明需要装配到IOC容器里的Bean对象
- 通过SpringBoot中”约定优于配置“这样的一个理念,去把这个配置类的全路径放在 classpath:/META-INF/Spring.factories文件里面,这样的话SpringBoot就可以知道第三方jia包里面这个配置类的位置,这个步骤主要是用到了Spring里面的SpringFactoriesLoader 来完成的。
- SpringBoot拿到所有的第三方jar包里面声明的配置类以后,通过Spring提供的importSelector这样一个接口来实现对这些配置类的动态加载,从而完成自动装配这样一个动作
在我看来呢,SpringBoot是"约定优于配置"这一理念下的产物,所以在很多的地方都会看到这一思想,他的出现让开发人员可以更加的聚焦在业务代码的编写上,而不需要去关心和业务无关的配置