Java外功核心(7)——深入源码,拆解Spring Bean作用域、生命周期与自动装配

Java外功核心(7)——深入源码,拆解Spring Bean作用域、生命周期与自动装配
SpringBoot版本:3.5.8

1.Bean的作用域

Spring Bean的作用域定义了Bean的作用范围,即Bean在哪些上下文中可用
/** * 实体类 */publicclassDog{}/** * 配置类 */@ConfigurationpublicclassDogConfig{//单例@Bean@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)publicDogsingleDog(){returnnewDog();}//原型@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)publicDogprototypeDog(){returnnewDog();}//请求@Bean@RequestScopepublicDogrequestDog(){returnnewDog();}//会话@Bean@SessionScopepublicDogsessionDog(){returnnewDog();}//应用@Bean@ApplicationScopepublicDogapplicationDog(){returnnewDog();}}/** * 启动类 */@SpringBootApplicationpublicclassSpringPrincipleApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SpringPrincipleApplication.class, args);}}/** * 视图层 */@RequestMapping("/test")@RestControllerpublicclassTestController{privatefinalDog single;privatefinalDog prototype;privatefinalDog request;privatefinalDog session;privatefinalDog application;privatefinalApplicationContext context;publicTestController(@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")publicStringsingle(){Dog singleDog =(Dog)context.getBean("singleDog");return"dog: "+this.single.toString()+"<br>"+"contextDog: "+ singleDog;}@RequestMapping("/prototype")publicStringprototype(){Dog prototypeDog =(Dog)context.getBean("prototypeDog");return"dog: "+this.prototype.toString()+"<br>"+"contextDog: "+ prototypeDog;}@RequestMapping("/request")publicStringrequest(){Dog requestDog =(Dog)context.getBean("requestDog");return"dog: "+this.request.toString()+"<br>"+"contextDog: "+ requestDog;}@RequestMapping("/session")publicStringsession(){Dog sessionDog =(Dog)context.getBean("sessionDog");return"dog: "+this.session.toString()+"<br>"+"contextDog: "+ sessionDog;}@RequestMapping("/application")publicStringapplication(){Dog applicationDog =(Dog)context.getBean("applicationDog");return"dog: "+this.application.toString()+"<br>"+"contextDog: "+ applicationDog;}}
    • 直接通过context.getBean()获取会触发新实例创建
    • 当使用@Autowired@Resource注入prototype作用域的Bean时:注入操作仅在初始化阶段(Spring容器创建和配置Bean的过程)执行一次,后续通过字段引用访问的是最初注入的实例,不会因后续请求自动重新注入新实例
    • 1.代理注入:当使用@Autowired注入Request作用域的Bean时,Spring实际上注入的是一个代理对象而非真实实例。代理对象在应用启动时就被注入到依赖它的单例Bean中,但真实实例的创建被延迟到HTTP请求发生时
    • 2.方法调用:注入的代理对象内部持有对当前HTTP请求上下文的引用。当调用代理对象的方法时,代理会从当前请求的上下文中查找或创建新的真实实例
    • 3.实例操作:虽然依赖注入发生在容器初始化阶段,但通过代理模式将实例的获取延迟到实际方法调用时,这种延迟查找机制确保每个请求线程都能获得独立的实例

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

在这里插入图片描述

Session(会话):每个用户会话创建一个Bean实例,仅在Web应用中有效

在这里插入图片描述

Request(请求):每个HTTP请求创建一个新的Bean实例,仅在Web应用中有效

在这里插入图片描述

Prototype(原型):每次请求Bean时都会创建一个新的实例

在这里插入图片描述

Singleton(单例):默认作用域,每个Spring容器中仅存在一个Bean实例

在这里插入图片描述

2. Bean的生命周期

生命周期指的是一个对象从创建到销毁的整个生命过程。Bean的生命周期分为以下5个部分:

  • 实例化:容器通过反射调用Bean的构造器创建对象实例
  • 属性赋值:容器注入依赖的属性值(例如@Autowired)
  • 初始化:
    • 通知方法调用:通过特定接口(如Spring的BeanNameAware)注入框架相关依赖或上下文信息
    • 前置处理:进行准备工作,例如参数校验、资源加载或权限检查。确保主逻辑具备执行条件,避免运行时错误
    • 初始化回调:在对象初始化阶段触发的自定义逻辑(如实现InitializingBean接口的afterPropertiesSet方法)。用于完成属性设置后的额外初始化操作
    • 后置处理:在流程结束后执行清理或结果处理(如AOP中的@After通知)
  • 使用Bean:Bean进入就绪状态,可被应用程序调用
  • 销毁Bean:容器关闭时触发销毁

2.1 示例

/** * 实体类 */publicclassCat{}/** * 配置类 */@ConfigurationpublicclassCatConfig{@BeanpublicCatcat(){returnnewCat();}}/** * 实现类 */@Component@Slf4jpublicclassBeanLifeComponentimplementsBeanNameAware,BeanPostProcessor,InitializingBean{privateCat cat;//1.实例化:执行构造方法publicBeanLifeComponent(){ log.info("1.实例化:执行构造方法");}//2.属性赋值:执行setter方法@AutowiredpublicvoidsetCat(Cat cat){ log.info("2.属性赋值:执行setter方法");this.cat = cat;}//3.1 通知方法调用@OverridepublicvoidsetBeanName(String name){ log.info("3.1 通知方法调用,bean name is {}", name);}//3.2 前置处理@OverridepublicObjectpostProcessBeforeInitialization(Object bean,String beanName)throwsBeansException{ log.info("3.3 前置处理,bean:{},beanName:{}", bean, beanName);returnBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}//3.3初始化回调@OverridepublicvoidafterPropertiesSet(){ log.info("3.2初始化回调");}//3.4后置处理@OverridepublicObjectpostProcessAfterInitialization(Object bean,String beanName)throwsBeansException{ log.info("3.4后置处理,bean:{},beanName:{}", bean, beanName);returnBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}//4.使用Beanpublicvoiduse(){ log.info("4.使用Bean");}//5.销毁Bean@PreDestroypublicvoidpreDestroy(){ log.info("5.销毁Bean");}}/** * 测试类 */@SpringBootTestclassSpringPrincipleApplicationTests{privatefinalApplicationContext context;@AutowiredpublicSpringPrincipleApplicationTests(ApplicationContext context){this.context = context;}@Testpublicvoidtest(){BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class); beanLifeComponent.use();}}
在这里插入图片描述
日志显示的顺序是:invokeInitMethods(初始化回调)applyBeanPostProcessorsBeforeInitialization(前置处理)applyBeanPostProcessorsAfterInitialization(后置处理)

这看起来与我上述介绍的生命周期流程相矛盾,具体解释放在源码解析后

2.2 源码解析

AbstractAutowireCapableBeanFactory 类的作用:主要负责Bean的创建、依赖注入以及初始化等生命周期管理
(该思维导图中的图片看不清楚,原图放在下述流程中)
  • 1.搜索AbstractAutowireCapableBeanFactory 类,并找到它的createBean方法
  • 3.doCreateBean方法中依次调用了createBeanInstancepopulateBeaninitializeBean方法

3.3.4

在这里插入图片描述

3.3.3

在这里插入图片描述

3.3.2

在这里插入图片描述

3.3.1

在这里插入图片描述

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

在这里插入图片描述

3.2

在这里插入图片描述

3.1

在这里插入图片描述

2.createBean方法中调用了doCreateBean方法

在这里插入图片描述

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外功精要(2)——Spring IoC&DI一文中有详细介绍。Spring通过五大注解 + @Bean可以将Bean加载到Ioc容器中,前提是这些注解类需要保证和SpringBoot启动类(@SpringBootApplication)在同一目录或者其子目录下

运行项目

在这里插入图片描述

编写代码

/** * 配置类 */@Slf4j@ComponentpublicclassTestConfig{publicvoiddemo(){ log.info("demo");}}/** * 测试类 */@SpringBootTestpublicclassSpringPrincipleApplicationTests{privatefinalApplicationContext context;@AutowiredpublicSpringPrincipleApplicationTests(ApplicationContext context){this.context = context;}@Testpublicvoiddemo(){TestConfig testConfig = context.getBean(TestConfig.class); testConfig.demo();}}

创建目录

在这里插入图片描述
下面介绍几种解决方法

3.1.1 @ComponentScan

作用:告诉Spring容器去哪里扫描那些被@Component、@Service、@Repository、@Controller等注解标记的类,并将它们自动注册为Bean

@SpringBootApplication@ComponentScan("com.example.springprincicle.component")publicclassSpringPrincipleApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SpringPrincipleApplication.class, args);}}
在这里插入图片描述

3.1.2 @Import

作用:用于将一个或多个配置类、组件类或其他类导入到当前的Spring应用上下文中

用法二:ImportSelector接口实现类

@Slf4j@ComponentpublicclassDemoConfig{publicvoiddemo(){ log.info("demo");}}publicclassMyImportSelectorimplementsImportSelector{@OverridepublicString[]selectImports(AnnotationMetadata importingClassMetadata){returnnewString[]{"com.example.springprincicle.component.TestConfig","com.example.springprincicle.component.DemoConfig"};}}@SpringBootTestpublicclassSpringPrincipleApplicationTests{privatefinalApplicationContext context;@AutowiredpublicSpringPrincipleApplicationTests(ApplicationContext context){this.context = context;}@Testpublicvoiddemo(){TestConfig testConfig = context.getBean(TestConfig.class); testConfig.demo();DemoConfig demoConfig = context.getBean(DemoConfig.class); demoConfig.demo();}}
在这里插入图片描述

用法一:导入单个类

@SpringBootApplication@Import(TestConfig.class)publicclassSpringPrincipleApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SpringPrincipleApplication.class, args);}}
在这里插入图片描述

3.1.3 自定义注解

在使用@Import注解导入Bean时,需要程序员熟悉第三方依赖的所有配置类、组件类或其他类,这对于程序员开发程序十分不友好。所以,应该由依赖的开发者来做这件事

比较常见的方案就是第三方依赖给我们提供一个注解,该注解内部封装@Import注解

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Import(MyImportSelector.class)public@interfaceEnableTestConfig{}@SpringBootApplication@EnableTestConfigpublicclassSpringPrincipleApplication{publicstaticvoidmain(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类@OverridepublicString[]selectImports(AnnotationMetadata annotationMetadata){if(!isEnabled(annotationMetadata)){returnNO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry =getAutoConfigurationEntry(annotationMetadata);returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protectedAutoConfigurationEntrygetAutoConfigurationEntry(AnnotationMetadata annotationMetadata){if(!isEnabled(annotationMetadata)){returnEMPTY_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);returnnewAutoConfigurationEntry(configurations, exclusions);}

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

//JDK源码,AutoConfigurationImportSelector类privatevoidfireAutoConfigurationImportEvents(List<String> configurations,Set<String> exclusions){List<AutoConfigurationImportListener> listeners =getAutoConfigurationImportListeners();if(!listeners.isEmpty()){AutoConfigurationImportEvent event =newAutoConfigurationImportEvent(this, configurations, exclusions);for(AutoConfigurationImportListener listener : listeners){invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event);}}}protectedList<AutoConfigurationImportListener>getAutoConfigurationImportListeners(){returnSpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,this.beanClassLoader);}publicstatic<T>List<T>loadFactories(Class<T> factoryType,@NullableClassLoader classLoader){returnforDefaultResourceLocation(classLoader).load(factoryType);}publicstaticSpringFactoriesLoaderforDefaultResourceLocation(@NullableClassLoader classLoader){returnforResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);}publicstaticfinalStringFACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";

遵循 Spring Boot 的"约定优于配置"理念,第三方依赖库需要将其自动配置类定义在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,这样 Spring Boot 启动时就能自动发现并加载这些配置

在这里插入图片描述

getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes):在Spring Boot 3.5.8(本文所用的版本)中,该方法不仅加载 Spring Boot 默认的自动配置类,还会加载所有第三方库提供的自动配置类

//JDK源码,AutoConfigurationImportSelector类protectedList<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;}

4.Gitee

Gitee地址:九转苍翎
本文源码:spring-principle

Read more

AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

文章目录 * 一、技术选型与准备 * 1.1 传统开发 vs AI生成 * 1.2 环境搭建与工具选择 * 1.3 DeepSeek API 初步体验 * 二、贪吃蛇游戏基础实现 * 2.1 游戏结构设计 * 2.2 初始化游戏 * 2.3 DeepSeek 生成核心逻辑 * 三、游戏功能扩展 * 3.1 多人联机模式 * 3.2 游戏难度动态调整 * 3.3 游戏本地保存与回放 * 3.4 跨平台移植 * 《Vue.js项目开发全程实录/软件项目开发全程实录》 * 编辑推荐 * 内容简介 * 作者简介 * 目录 一、

By Ne0inhk
DeepSeek-R1是真码农福音?我们问了100位开发者……

DeepSeek-R1是真码农福音?我们问了100位开发者……

从GitHub Copilot到DeepSeek-R1,AI编程工具正在引发一场"效率革命",开发者们对这些工具的期待与质疑并存。据Gartner预测,到2028年,将有75%的企业软件工程师使用AI代码助手。 眼看着今年国产选手DeepSeek-R1凭借“深度思考”能力杀入战场,它究竟是真码农福音还是需要打补丁的"潜力股"? ZEEKLOG问卷调研了社区内来自全栈开发、算法工程师、数据工程师、前端、后端等多个技术方向的100位开发者(截止到2月25日),聚焦DeepSeek-R1的代码生成效果、编写效率、语法支持、IDE集成、复杂代码处理等多个维度,一探DeepSeek-R1的开发提效能力。 代码生成效果:有成效但仍需提升 * 代码匹配比例差强人意 在代码生成与实际需求的匹配方面,大部分开发者(58人)遇到生成代码与实际需求完全匹配无需修改的比例在40%-70%区间,12人遇到代码匹配比例在70%-100%这样较高的区间。 然而,有30人代码匹配比例低于40%。这说明DeepSeek-R1在代码生成方面有一定效果,但在部分复杂或特定场景下,仍有很大的提升空间。

By Ne0inhk
[DeepSeek] 入门详细指南(上)

[DeepSeek] 入门详细指南(上)

前言 今天的是 zty 写DeepSeek的第1篇文章,这个系列我也不知道能更多久,大约是一周一更吧,然后跟C++的知识详解换着更。 来冲个100赞兄弟们 最近啊,浙江出现了一匹AI界的黑马——DeepSeek。这个名字可能对很多人来说还比较陌生,但它已经在全球范围内引发了巨大的关注,甚至让一些科技巨头感到了压力。简单来说这 DeepSeek足以改变世界格局                                                   先   赞   后   看    养   成   习   惯  众所周知,一篇文章需要一个头图                                                   先   赞   后   看    养   成   习   惯   上面那行字怎么读呢,让大家来跟我一起读一遍吧,先~赞~后~看~养~成~习~惯~ 想要 DeepSeek从入门到精通.pdf 文件的加这个企鹅群:953793685(

By Ne0inhk
DeepFace深度学习库+OpenCV实现——情绪分析器

DeepFace深度学习库+OpenCV实现——情绪分析器

目录 应用场景 实现组件 1. 硬件组件 2. 软件库与依赖 3. 功能模块 代码详解(实现思路) 导入必要的库 打开摄像头并初始化变量 主循环 FPS计算 情绪分析及结果展示 显示FPS和图像 退出条件 编辑 完整代码 效果展示 自然的 开心的 伤心的 恐惧的 惊讶的  效果展示 自然的 开心的 伤心的 恐惧的 惊讶的   应用场景         应用场景比较广泛,尤其是在需要了解和分析人类情感反应的场合。: 1. 心理健康评估:在心理健康领域,可以通过长期监控和分析一个人的情绪变化来辅助医生进行诊断或治疗效果评估。 2. 用户体验研究:在产品设计、广告制作或网站开发过程中,通过观察用户在使用过程中的情绪反应,来优化产品的用户体验。 3. 互动娱乐:在游戏或虚拟现实应用中,根据玩家的情绪状态动态调整游戏难度或故事情节,以增加沉浸感和互动性。

By Ne0inhk