Spring Bean作用域、生命周期与自动装配深入解析

 更新时间:2025年12月24日 15:19:07   作者:九转苍翎  
本文详细介绍了SpringBoot中Bean的作用域、生命周期以及自动装配的概念,Bean的作用域包括Singleton、Prototype、Request、Session和Application,文章还解析了SpringBoot的自动配置机制和Gitee上的相关资源,感兴趣的朋友跟随小编一起看看吧

SpringBoot版本:3.5.8

1.Bean的作用域

Spring Bean的作用域定义了Bean的作用范围,即Bean在哪些上下文中可用

/**
 * 实体类
 */
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() + "
" + "contextDog: " + singleDog;
    }
    @RequestMapping("/prototype")
    public String prototype(){
        Dog prototypeDog = (Dog)context.getBean("prototypeDog");
        return "dog: " + this.prototype.toString() + "
" + "contextDog: " + prototypeDog;
    }
    @RequestMapping("/request")
    public String request(){
        Dog requestDog = (Dog)context.getBean("requestDog");
        return "dog: " + this.request.toString() + "
" + "contextDog: " + requestDog;
    }
    @RequestMapping("/session")
    public String session(){
        Dog sessionDog = (Dog)context.getBean("sessionDog");
        return "dog: " + this.session.toString() + "
" + "contextDog: " + sessionDog;
    }
    @RequestMapping("/application")
    public String application(){
        Dog applicationDog = (Dog)context.getBean("applicationDog");
        return "dog: " + this.application.toString() + "
" + "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 示例

/**
 * 实体类
 */
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方法中依次调用了createBeanInstancepopulateBeaninitializeBean方法

3.1

3.2

3.3 初始化:initializeBean方法中依次调用了invokeAwareMethodsapplyBeanPostProcessorsBeforeInitializationinvokeInitMethodsapplyBeanPostProcessorsAfterInitialization方法

3.3.1

3.3.2

3.3.3

3.3.4

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的原理

创建目录

编写代码

/**
 * 配置类
 */
@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

@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应用上下文中

用法一:导入单个类

@SpringBootApplication
@Import(TestConfig.class)
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

用法二:ImportSelector接口实现类

@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注解

@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 应用启动时动态加载自动配置类

    //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 默认的自动配置类,还会加载所有第三方库提供的自动配置类
	//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 启动时就能自动发现并加载这些配置

fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions):体现了Spring Framework/Spring Boot的向后兼容性设计。即使在 Spring Boot 3.x 中引入了新的 .imports 机制,Spring Framework 仍然保留了 spring.factories 的支持

    //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

到此这篇关于拆解Spring Bean作用域、生命周期与自动装配的文章就介绍到这了,更多相关Spring Bean作用域、生命周期与自动装配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot2.0如何通过fastdfs实现文件分布式上传

    springboot2.0如何通过fastdfs实现文件分布式上传

    这篇文章主要介绍了springboot2.0如何通过fastdfs实现文件分布式上传,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • spring源码阅读--@Transactional实现原理讲解

    spring源码阅读--@Transactional实现原理讲解

    这篇文章主要介绍了spring源码阅读--@Transactional实现原理讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 浅谈JAVA 责任链模式

    浅谈JAVA 责任链模式

    这篇文章主要介绍了JAVA 责任链模式的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • 基于Java中进制的转换函数详解

    基于Java中进制的转换函数详解

    下面小编就为大家带来一篇基于Java中进制的转换函数详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Spring Boot整合流控组件Sentinel的场景分析

    Spring Boot整合流控组件Sentinel的场景分析

    Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑,这篇文章主要介绍了Spring Boot整合流控组件Sentinel的过程解析,需要的朋友可以参考下
    2021-12-12
  • 关于maven install报错原因揭秘:parent.relativePath指向错误的本地POM文件

    关于maven install报错原因揭秘:parent.relativePath指向错误的本地POM文件

    在使用Maven进行项目构建时,如果遇到'parent.relativePath'指向错误的本地POM文件的问题,可能会导致构建失败,这通常是由于父项目POM文件的相对路径设置错误、本地POM文件与父项目POM文件版本或内容不一致所致,解决方法包括检查并修正父项目POM文件中的相对路径设置
    2024-09-09
  • 新手初学Java List 接口

    新手初学Java List 接口

    这篇文章主要介绍了Java集合操作之List接口及其实现方法,详细分析了Java集合操作中List接口原理、功能、用法及操作注意事项,需要的朋友可以参考下
    2021-07-07
  • MyBatis自定义TypeHandler实现字段加密解密

    MyBatis自定义TypeHandler实现字段加密解密

    本文主要介绍了MyBatis自定义TypeHandler实现字段加密解密,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • Spring Boot中调用外部接口的3种方式步骤

    Spring Boot中调用外部接口的3种方式步骤

    这篇文章主要给大家介绍了关于Spring Boot中调用外部接口的3种方式步骤,在Spring-Boot项目开发中,存在着本模块的代码需要访问外面模块接口,或外部url链接的需求,需要的朋友可以参考下
    2023-08-08
  • Mybatis-Plus实现自动生成代码的操作步骤

    Mybatis-Plus实现自动生成代码的操作步骤

    AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率,本文将给大家介绍Mybatis-Plus实现自动生成代码的操作步骤
    2023-10-10

最新评论