文章目录
Spring的Bean生命周期
Spring三级缓存的学习需要有对Spring的Bean生命周期的理解:
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
【新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题)】 https://www.bilibili.com/video/BV1yT411H7YK/?p=39&share_source=copy_web&vd_source=afbacdc02063c57e7a2ef256a4db9d2a
一般来说Bean的定义和注册现在很少用XML方式来搞,这么搞太复杂了,现在一般都是要用@Configuration注解的类来完成XML文件的Bean注册。
至于具体怎么用@Configuration来搞注册Bean的类,可以看下面这个视频,然后后头再来看本博客应该就能有清晰的了解了
【黑马程序员SpringBoot3+Vue3全套视频教程,springboot+vue企业级全栈开发从基础、实战到面试一套通关】 https://www.bilibili.com/video/BV14z4y1N7pg/?p=9&share_source=copy_web&vd_source=afbacdc02063c57e7a2ef256a4db9d2a
@Configuration
public class CommonConfig {
//该方法负责注册Bean到IOC容器里面,当然你也可以@Bean多个方法来注册多个Bean
@Bean public StrUtil str(){//StrUtil是第三方导入的包
return new StrUtil();
}
// 注册多个Bean
@Bean public StrUtil2 str2(){//StrUtil是第三方导入的包
return new StrUtil2();
}
// 注册多个Bean
@Bean public StrUtil3 str3(){//StrUtil是第三方导入的包
return new StrUtil3();
}
}
ApplicationContext 这里是SpringBoot的IOC容器,里面是有各种各样的依赖注入的Bean对象,而Bean对象的注入可以用@Autowire来注入,@Configuration的配置类来@Bean注入,这里讲解的就是第二种!
@SpringBootApplication
public class App {
public static void main(String[] args){
ApplicationContext appContext = SpringApplication.run(App.class, args);
StrUtil obj = appContext.getBean(StrUtil.class);
System.out.println(obj);
}
}
在Spring框架中,@Configuration
注解和@Bean注解有着紧密的联系,它们共同用于定义Spring容器中的Bean。
-
@Configuration注解:
@Configuration
是一个类级别的注解,用于声明一个类作为配置类,相当于传统XML配置文件中的标签。- 被
@Configuration
注解的类内部通常包含一个或多个@Bean注解的方法。 @Configuration
注解的类可以包含构造函数、普通方法、成员变量等,它们可以定义和注册多个Bean。@Configuration
类可以被组织成层级结构,允许继承和组合,这样可以更灵活地组织配置逻辑。
-
@Bean注解:
@Bean
是一个方法级别的注解,用于声明一个方法的返回值应该被注册为Spring容器中的一个Bean。- 被
@Bean
注解的方法通常会执行一些初始化逻辑,并返回一个对象实例,这个实例随后会被Spring容器管理。 @Bean
注解可以指定Bean的名称,如果不指定,则默认使用方法的名称作为Bean的名称。@Bean
注解可以与@Configuration
注解配合使用,也可以与@Component
注解配合使用,后者用于定义组件类,而@Bean
用于显式地声明组件类的实例为Bean。
联系
@Configuration
注解的类提供了一个上下文环境,允许开发者使用Java代码来定义Bean,而不是XML配置文件。@Bean
注解用于在@Configuration
注解的类中声明具体的Bean。每个@Bean注解的方法都会告诉Spring容器,这个方法的返回值应该被注册为一个Bean。@Configuration
类中可以包含多个@Bean注解的方法,每个方法都可以定义一个或多个Bean。@Configuration
和@Bean注解的结合使用,使得Spring框架能够以编程的方式定义和管理Bean,这被称为Java配置(Java-based Configuration)。
总结来说,@Configuration
注解定义了一个配置类,而@Bean注解定义了配置类中具体的方法,这些方法返回的对象会被注册为Spring容器中的Bean。这种机制提供了一种声明式的方式来配置Spring应用上下文,使得配置更加清晰和易于管理。
Spring三级缓存无法解决‘构造函数级别 ’的循环依赖(这种只能用@Lazy延迟加载解决)
Spring三级缓存解决的是‘初始化赋值阶段 的循环依赖,也就是通过set方法来初始化的阶段的循环依赖
面对以下情况的循环依赖问题,这只是
@Aotuowire级别的循环依赖,不是构造函数级别的循环依赖!可以用Spring三级缓存解决!
构造函数级别的循环依赖:↓↓↓↓↓↓↓↓。只能用@Lazy
Spring的Bean生命周期↓↓↓↓↓↓↓↓
总结↓↓↓↓↓↓↓↓
Spring三级缓存
Spring的三级缓存机制是解决循环依赖问题的关键。三级缓存的设计主要是为了在Bean的创建过程中,能够有效地处理单例Bean之间的循环依赖。以下是三级缓存的详细解释:
-
一级缓存(singletonObjects):
- 这是一个存储已经完全初始化的Bean的缓存。当一个Bean被完全创建和初始化后,它会被添加到这个缓存中。这个缓存确保了在后续的依赖查找中,可以直接获取到已经完成的Bean实例。
-
二级缓存(earlySingletonObjects):
- 这个缓存存储的是早期暴露的对象,即那些已经被构造函数创建但尚未完成属性填充和初始化的Bean。这些对象在Bean创建过程中被放入二级缓存,以便其他Bean可以引用它们,从而解决循环依赖问题。
-
三级缓存(singletonFactories):
- 三级缓存用于存储Bean‘工厂对象’,这些工厂对象能够在Bean完全初始化之前生成Bean实例。当一个Bean正在被创建时,如果它依赖于另一个尚未完全初始化的Bean,Spring会从三级缓存中获取相应的工厂对象,并调用它来生成早期暴露的Bean实例。
循环依赖的解决过程
当Spring容器创建Bean时,如果发现需要依赖的Bean尚未完全初始化,它会将当前Bean的半成品放入三级缓存中,并继续创建依赖的Bean。如果依赖的Bean也需要当前Bean,则Spring会从二级缓存中查找早期暴露的对象,确保能够正确地解决循环依赖。
这种三级缓存机制不仅解决了单例Bean之间的循环依赖问题,还能够处理原型Bean之间的循环依赖,因为原型Bean的每次请求都会创建新的实例,而三级缓存确保了即使在创建过程中也能引用到正在创建的Bean的早期暴露对象。
结论
通过引入三级缓存,Spring在处理复杂的依赖关系时变得更加灵活和强大,能够有效地解决循环依赖问题,确保应用的正常运行。
如果对象被增强了,有什么代理对象之类的,需要使用到三级缓存!
流程:
- 先去实例化A,生成A的对象工厂ObjectFactory,吧工厂放到三级缓存池中
- 这时候A依赖B,需要注入B,B对象不存在,此时实例化B,然后B生成一个ObjectFactory,存入三级缓存池里
- 此时需要注入A,然后我们从三级缓存池里面取出A的对象工厂,由工厂判断此时需要的是A的代理对象还是A?比如这里需要的是A的代理对象,ok那么我们ObjectFactory生成A的代理对象,这个工厂生成的A的代理对象存到二级缓存池(earlySingleton),再吧A的代理对象注入到B
- 然后B创建成功,把B丢到一级缓存池里面,再把B注入到A里面,然后A也创建成功,再把A丢到一级缓存池里面
- 最后把二级缓存里面的所有半成品清除
Q&A
Q:二级缓存是否是多余的?上面这个图只有一级缓存和三级缓存也能解决循环依赖,还能解决代理对象的问题了
A:不是的,一般来说,我们的对象都是单例的,在通过A的对象工厂ObjectFactory创建代理对象时 ,创建完成之后丢到二级缓存里,将B注入给A的时候,就需要从二级缓存里面获取A,从哪里获取呢?答案是二级缓存池里面!如果反复创建了代理对象,那么如果有二级缓存池的话,第一次创建完毕之后可以放到二级缓存池里面,以后再次需要的时候就可以直接拿出来,无需重复创建!
Q:光有一级缓存能不能解决@Autowire级别的循环依赖问题?
A:不能,一级缓存是要经历完整Bean生命周期才能丢到一级缓存里面的,但是循环依赖问题是A依赖B,B依赖A,此时A和B都没有经历完整的生命周期,都无法丢到一级缓存里面
Q:光有一级缓存和二级缓存,能不能解决@Autowire级别的循环依赖问题?
A:可以的,可以解决大部分循环依赖问题,但是如果A被增强过了,那就不能了!
如果A没有被增强过:
流程如下:↓
- 先创建A,把A的半成品丢到二级缓存earlySingletonObjects
- 此时需要注入B,B不存在,创建B,把半成品B丢到二级缓存里面
- 半成品B需要A,把之前二级缓存里面半成品A拿出来组成成品B,B历经完整Bean生命周期,丢到一级缓存里面
- 再把B注入A,此时A也完整了,丢到一级缓存里面。
- 最后把二级缓存里面的所有半成品清除
Spring自动装配原理
@Import(ClassA.class)的意思是,ClassA.class一般是某个带有@Configuration的配置类,负责Bean的注册导入,@Import(ClassA.class)的意思是导入这个配置类,来完成第三方Bean的导入使用。
@Import(ClassA.class)的豪华升级装逼版是@Import(AutoConfigurationImportSelector.class),导入指定Meta-INF里面的spring.factories
里面的所有类名,每个类都是带有条件的注解,比如下面这个@ConditionalOnClass
和@ConditionalOnMissingBean
@ConditionalOnClass
@ConditionalOnClass
是Spring框架中用于条件化配置的一个注解,它的作用是仅当指定的类存在于项目的类路径(classpath)中时,才会使标注了该注解的类或方法生效。这个注解可以用在类上,也用在方法上,特别是用在带有@Configuration注解的类和带有@Bean注解的方法上。
具体来说,@ConditionalOnClass
注解有两个属性:
-
value()
:接收一个Class
对象数组,表示必须存在于类路径上的类。只有当这些类都被找到时,条件才会成立。 -
name()
:接收一个字符串数组,表示必须存在于类路径上的类的全限定名称。这个属性允许你不通过Class
对象来指定类,而是直接使用类的全限定名。
@ConditionalOnClass
注解的工作原理是通过OnClassCondition
类来实现的,这个类实现了Condition
接口,并在matches
方法中判断指定的类是否存在于类路径中。如果条件成立(即指定的类存在),则相关的配置类或Bean将会被创建并注册到Spring容器中;如果不成立,则这些配置将被忽略。
在实际开发中,@ConditionalOnClass
注解非常有用,它允许开发者根据类路径中是否存在某些类来动态地启用或禁用特定的配置。例如,如果一个项目中包含了某个特定的库或框架,可能需要加载一些特定的配置来集成这个库或框架的功能,这时就可以使用@ConditionalOnClass
来实现这种条件化配置。
说白了类似于C里面的条件编译语句#ifndef
和#ifdef
@ConditionalOnMissingBean
@ConditionalOnMissingBean
是Spring框架中用于条件化配置的一个注解,它的作用是在容器中没有找到指定类型的Bean时,才会创建并注册被该注解标记的Bean。以下是@ConditionalOnMissingBean
注解的主要特点和使用方式:
-
作用位置:
@ConditionalOnMissingBean
注解可以作用在带有@Bean
注解的方法上,也可以作用在带有@Configuration
注解的类上。它用于控制Bean的创建过程,确保在没有其他相同类型的Bean被定义时,才创建该Bean。 -
保证唯一性:
@ConditionalOnMissingBean
注解确保Spring容器中只有一个特定类型的Bean实例。当存在多个相同类型的Bean实现时,该注解可以帮助选择或提供一个默认的实现。 -
使用方式:在定义Bean的方法上使用
@ConditionalOnMissingBean
注解,并指定需要检查是否存在的Bean的类型。如果该类型的Bean尚未被注册到容器中,则该方法会被调用,从而注册一个新的Bean实例。 -
与继承关系:
@ConditionalOnMissingBean
不仅会检查直接的类匹配,还会检查类的继承树。如果容器中存在参数中指定的类或其子类、父类,@ConditionalOnMissingBean
会返回false
,使得标注的类不执行。 -
扩展性:
@ConditionalOnMissingBean
在封装组件时特别有用,它允许开发者提供一个默认的实现类,并在没有其他自定义Bean时使用这个默认实现。这样可以提高组件的可扩展性,允许用户根据业务需求定义自己的Bean。 -
自动配置:在Spring Boot中,
@ConditionalOnMissingBean
常用于自动配置类中,以确保只有在没有用户定义的Bean时,才加载默认的自动配置。
总结来说,@ConditionalOnMissingBean
是一个强大的条件注解,它帮助开发者在Spring应用中实现更灵活和可配置的Bean管理。通过使用这个注解,可以避免不必要的Bean创建,同时提供默认实现,增强了应用的灵活性和可维护性。