SpringBoot版本:3.5.8
1.Bean的作用域
Spring Bean的作用域定义了Bean的作用范围,即Bean在哪些上下文中可用
java
/**
* 实体类
*/
public class Dog {
}
/**
* 配置类
*/
@Configuration
public class DogConfig {
//单例
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singleDog(){
return new Dog();
}
//原型
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototypeDog(){
return new Dog();
}
//请求
@Bean
@RequestScope
public Dog requestDog(){
return new Dog();
}
//会话
@Bean
@SessionScope
public Dog sessionDog(){
return new Dog();
}
//应用
@Bean
@ApplicationScope
public Dog applicationDog(){
return new Dog();
}
}
/**
* 启动类
*/
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
/**
* 视图层
*/
@RequestMapping("/test")
@RestController
public class TestController {
private final Dog single;
private final Dog prototype;
private final Dog request;
private final Dog session;
private final Dog application;
private final ApplicationContext context;
public TestController(@Qualifier("singleDog") Dog single, @Qualifier("prototypeDog") Dog prototype,
@Qualifier("requestDog") Dog request, @Qualifier("sessionDog") Dog session,
@Qualifier("applicationDog") Dog application, ApplicationContext context) {
this.single = single;
this.prototype = prototype;
this.request = request;
this.session = session;
this.application = application;
this.context = context;
}
@RequestMapping("/single")
public String single(){
Dog singleDog = (Dog)context.getBean("singleDog");
return "dog: " + this.single.toString() + "<br>" + "contextDog: " + singleDog;
}
@RequestMapping("/prototype")
public String prototype(){
Dog prototypeDog = (Dog)context.getBean("prototypeDog");
return "dog: " + this.prototype.toString() + "<br>" + "contextDog: " + prototypeDog;
}
@RequestMapping("/request")
public String request(){
Dog requestDog = (Dog)context.getBean("requestDog");
return "dog: " + this.request.toString() + "<br>" + "contextDog: " + requestDog;
}
@RequestMapping("/session")
public String session(){
Dog sessionDog = (Dog)context.getBean("sessionDog");
return "dog: " + this.session.toString() + "<br>" + "contextDog: " + sessionDog;
}
@RequestMapping("/application")
public String application(){
Dog applicationDog = (Dog)context.getBean("applicationDog");
return "dog: " + this.application.toString() + "<br>" + "contextDog: " + applicationDog;
}
}
- Singleton(单例) :默认作用域,每个Spring容器中仅存在一个Bean实例

- Prototype(原型) :每次请求Bean时都会创建一个新的实例
- 直接通过context.getBean()获取会触发新实例创建
- 当使用
@Autowired或@Resource注入prototype作用域的Bean时:注入操作仅在初始化阶段(Spring容器创建和配置Bean的过程)执行一次,后续通过字段引用访问的是最初注入的实例,不会因后续请求自动重新注入新实例
- Request(请求) :每个HTTP请求创建一个新的Bean实例,仅在Web应用中有效
- 1.代理注入:当使用@Autowired注入Request作用域的Bean时,Spring实际上注入的是一个代理对象而非真实实例。代理对象在应用启动时就被注入到依赖它的单例Bean中,但真实实例的创建被延迟到HTTP请求发生时
- 2.方法调用:注入的代理对象内部持有对当前HTTP请求上下文的引用。当调用代理对象的方法时,代理会从当前请求的上下文中查找或创建新的真实实例
- 3.实例操作:虽然依赖注入发生在容器初始化阶段,但通过代理模式将实例的获取延迟到实际方法调用时,这种延迟查找机制确保每个请求线程都能获得独立的实例
- Session(会话) :每个用户会话创建一个Bean实例,仅在Web应用中有效

- Application(应用) :整个Web应用共享一个Bean实例

2. Bean的生命周期
生命周期指的是一个对象从创建到销毁的整个生命过程。Bean的生命周期分为以下5个部分:
实例化:容器通过反射调用Bean的构造器创建对象实例属性赋值:容器注入依赖的属性值(例如@Autowired)初始化:- 通知方法调用:通过特定接口(如Spring的BeanNameAware)注入框架相关依赖或上下文信息
- 前置处理:进行准备工作,例如参数校验、资源加载或权限检查。确保主逻辑具备执行条件,避免运行时错误
- 初始化回调:在对象初始化阶段触发的自定义逻辑(如实现InitializingBean接口的afterPropertiesSet方法)。用于完成属性设置后的额外初始化操作
- 后置处理:在流程结束后执行清理或结果处理(如AOP中的@After通知)
使用Bean:Bean进入就绪状态,可被应用程序调用销毁Bean:容器关闭时触发销毁
2.1 示例
java
/**
* 实体类
*/
public class Cat {
}
/**
* 配置类
*/
@Configuration
public class CatConfig {
@Bean
public Cat cat(){
return new Cat();
}
}
/**
* 实现类
*/
@Component
@Slf4j
public class BeanLifeComponent implements BeanNameAware, BeanPostProcessor, InitializingBean {
private Cat cat;
//1.实例化:执行构造方法
public BeanLifeComponent() {
log.info("1.实例化:执行构造方法");
}
//2.属性赋值:执行setter方法
@Autowired
public void setCat(Cat cat) {
log.info("2.属性赋值:执行setter方法");
this.cat = cat;
}
//3.1 通知方法调用
@Override
public void setBeanName(String name) {
log.info("3.1 通知方法调用,bean name is {}", name);
}
//3.2 前置处理
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.info("3.3 前置处理,bean:{},beanName:{}", bean, beanName);
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
//3.3初始化回调
@Override
public void afterPropertiesSet() {
log.info("3.2初始化回调");
}
//3.4后置处理
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("3.4后置处理,bean:{},beanName:{}", bean, beanName);
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
//4.使用Bean
public void use(){
log.info("4.使用Bean");
}
//5.销毁Bean
@PreDestroy
public void preDestroy(){
log.info("5.销毁Bean");
}
}
/**
* 测试类
*/
@SpringBootTest
class SpringPrincipleApplicationTests {
private final ApplicationContext context;
@Autowired
public SpringPrincipleApplicationTests(ApplicationContext context) {
this.context = context;
}
@Test
public void test(){
BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
beanLifeComponent.use();
}
}

日志显示的顺序是:
- invokeInitMethods(初始化回调)
- applyBeanPostProcessorsBeforeInitialization(前置处理)
- applyBeanPostProcessorsAfterInitialization(后置处理)
这看起来与我上述介绍的生命周期流程相矛盾,具体解释放在源码解析后
2.2 源码解析
AbstractAutowireCapableBeanFactory 类的作用:主要负责Bean的创建、依赖注入以及初始化等生命周期管理
(该思维导图中的图片看不清楚,原图放在下述流程中)
- 1.搜索AbstractAutowireCapableBeanFactory 类,并找到它的createBean方法
- 2.createBean方法中调用了doCreateBean方法

- 3.doCreateBean方法中依次调用了
createBeanInstance、populateBean和initializeBean方法- 3.1

- 3.2

- 3.3 初始化:initializeBean方法中依次调用了
invokeAwareMethods、applyBeanPostProcessorsBeforeInitialization、invokeInitMethods和applyBeanPostProcessorsAfterInitialization方法
- 3.3.1

- 3.3.2

- 3.3.3

- 3.3.4

- 3.3.1
- 3.1
2.3 日志与源码"冲突"的原因分析
根据源码来看,正确的生命周期流程是:前置处理 → 初始化回调 → 后置处理。与上述我运行输出的日志相矛盾
关键点:BeanPostProcessor本身也是Bean,当Spring初始化一个BeanPostProcessor实现类(BeanLifeComponent类)时,这个过程是递归的
执行流程分析:
-
1.创建非BeanPostProcessor Bean(BeanLifeComponent类):
- 按照源码顺序正常执行:BeforeInitialization → InitMethods → AfterInitialization
-
2.创建BeanPostProcessor Bean时:
-
Spring需要先让这个BeanPostProcessor对象本身完成初始化(调用invokeInitMethods)
-
然后才能将它加入到BeanPostProcessor列表中,供后续其他Bean(BeanLifeComponent类)使用
-
但对于这个BeanPostProcessor Bean自己来说:
-
它自己的afterPropertiesSet方法会在invokeInitMethods中执行(初始化回调阶段)
-
但它自己的postProcessBeforeInitialization(前置处理)/postProcessAfterInitialization(后置)方法不会在它自己的创建过程中被调用!
-
-
上述日志中的情况解释:日志显示的是 一个BeanPostProcessor实现类(BeanLifeComponent类)的初始化过程:
- 3.2 初始化回调 - 这个BeanPostProcessor Bean自己的afterPropertiesSet()
- 3.3 前置处理 - BeanLifeComponent类对这个BeanPostProcessor Bean的处理
- 3.4 后置处理 - BeanLifeComponent类对这个BeanPostProcessor Bean的处理
3.SpringBoot自动装配
作用:自动注册Bean到Spring容器,不需要手动配置,通过约定大于配置 的方式减少手动配置的复杂性。换言之,SpringBoo的自动配置就是将依赖Jar包中的配置类 以及Bean 加载到Ioc容器的过程
3.1 SpringBoot加载Bean
在pom.xml文件中引入第三方依赖,实际上就是将第三方代码引入到SpringBoot项目中。SpringBoot项目在启动时能识别这些依赖并自动将它们的配置类以及Bean加载到Ioc容器的过程。下面,我将编写代码作为第三方依赖,深入解析SpringBoot加载Bean的原理
-
创建目录

-
编写代码
java/** * 配置类 */ @Slf4j @Component public class TestConfig { public void demo(){ log.info("demo"); } } /** * 测试类 */ @SpringBootTest public class SpringPrincipleApplicationTests { private final ApplicationContext context; @Autowired public SpringPrincipleApplicationTests(ApplicationContext context) { this.context = context; } @Test public void demo(){ TestConfig testConfig = context.getBean(TestConfig.class); testConfig.demo(); } } -
运行项目

-
错误日志解析:在Java外功精要(2)------Spring IoC&DI一文中有详细介绍。Spring通过
五大注解 + @Bean可以将Bean加载到Ioc容器中,前提是这些注解类需要保证和SpringBoot启动类(@SpringBootApplication)在同一目录或者其子目录下
下面介绍几种解决方法
3.1.1 @ComponentScan
作用:告诉Spring容器去哪里扫描那些被@Component、@Service、@Repository、@Controller等注解标记的类,并将它们自动注册为Bean
java
@SpringBootApplication
@ComponentScan("com.example.springprincicle.component")
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}

3.1.2 @Import
作用:用于将一个或多个配置类、组件类或其他类导入到当前的Spring应用上下文中
-
用法一 :导入单个类
java@SpringBootApplication @Import(TestConfig.class) public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
-
用法二 :ImportSelector接口实现类
java@Slf4j @Component public class DemoConfig { public void demo(){ log.info("demo"); } } public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.springprincicle.component.TestConfig", "com.example.springprincicle.component.DemoConfig"}; } } @SpringBootTest public class SpringPrincipleApplicationTests { private final ApplicationContext context; @Autowired public SpringPrincipleApplicationTests(ApplicationContext context) { this.context = context; } @Test public void demo(){ TestConfig testConfig = context.getBean(TestConfig.class); testConfig.demo(); DemoConfig demoConfig = context.getBean(DemoConfig.class); demoConfig.demo(); } }
3.1.3 自定义注解
在使用@Import注解导入Bean时,需要程序员熟悉第三方依赖的所有配置类、组件类或其他类,这对于程序员开发程序十分不友好。所以,应该由依赖的开发者来做这件事
-
比较常见的方案就是第三方依赖给我们提供一个注解,该注解内部封装
@Import注解java@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(MyImportSelector.class) public @interface EnableTestConfig { } @SpringBootApplication @EnableTestConfig public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
3.2 @SpringBootApplication源码解析
@SpringBootApplication是一个组合注解,包含以下三个核心注解的功能
3.2.1 @ComponentScan

- 排除不需要扫描的配置类,防止自动配置类被重复扫描或注册
- 在
@SpringBootApplication注解中,@ComponentScan注解没有指定扫描路径,那么默认扫描路径为 @SpringBootApplication标注的类的类路径
3.2.2 @SpringBootConfiguration

- 标记该类为Spring的配置类
- @Indexed:为Spring的组件扫描提供索引支持,加速应用启动时的类加载过程
3.2.3 @EnableAutoConfiguration
AutoConfigurationImportSelector类:它实现了 DeferredImportSelector 接口,负责在Spring 应用启动时动态加载自动配置类
java
//JDK源码,AutoConfigurationImportSelector类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
-
getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes):在Spring Boot 3.5.8(本文所用的版本)中,该方法不仅加载 Spring Boot 默认的自动配置类,还会加载所有第三方库提供的自动配置类java//JDK源码,AutoConfigurationImportSelector类 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader());//扫描类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件 List<String> configurations = importCandidates.getCandidates();//从 ImportCandidates 对象中获取所有找到的自动配置类名,这些类名是从 .imports 文件中读取的 Assert.state(!CollectionUtils.isEmpty(configurations), "No auto configuration classes found in " + "META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }- 遵循 Spring Boot 的"约定优于配置"理念,第三方依赖库需要将其自动配置类定义在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,这样 Spring Boot 启动时就能自动发现并加载这些配置

- 遵循 Spring Boot 的"约定优于配置"理念,第三方依赖库需要将其自动配置类定义在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,这样 Spring Boot 启动时就能自动发现并加载这些配置
-
fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions):体现了Spring Framework/Spring Boot 的向后兼容性设计。即使在 Spring Boot 3.x 中引入了新的 .imports 机制,Spring Framework 仍然保留了 spring.factories 的支持java//JDK源码,AutoConfigurationImportSelector类 private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) { List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } } protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader); } public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) { return forDefaultResourceLocation(classLoader).load(factoryType); } public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) { return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader); } public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
4.Gitee
Gitee地址:九转苍翎
本文源码:spring-principle


