当前位置: 首页 > news >正文

请你谈谈:spring bean的生命周期 - 阶段2:Bean实例化阶段

在Spring框架中,Bean的实例化是Bean生命周期中的一个重要阶段。这个过程包括两个关键的子阶段:Bean实例化前阶段和Bean实例化阶段本身。

BeanFactoryPostProcessor:BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中的一个个的BeanDefination进行一定程度上的修改与替换。例如使用占位符配置元信息,例如配置Jdbc的DataSource连接的时候可以这样配置:

 <property name="driverClassName"  value="${jdbc.driverClassName}">  </property>  

BeanFactoryPostProcessor就会对注册到BeanDefinationRegistry中的BeanDefination做最后的修改,替换$占位符为配置文件中的真实的数据。至此,整个容器启动阶段就算完成了,容器的启动阶段的最终产物就是注册到BeanDefinationRegistry中的一个个BeanDefination了,这就是Spring为Bean实例化所做的预热的工作:

在这里插入图片描述
BeanFactoryPostProcessor 是 Spring 框架中的一个高级接口,允许在 Spring IoC 容器完全初始化之前(准确点说应该是:实例化之前),对 BeanFactory 中的 Bean 定义(BeanDefinition)进行修改。当 Spring 容器启动时,它会检测并调用所有实现了 BeanFactoryPostProcessor 接口的 bean,为开发者提供了在容器启动过程中定制和修改容器内部结构的机会。

BeanDefinitionRegistryBeanFactory 的一个子接口,提供了动态注册 BeanDefinition 的能力。结合使用 BeanFactoryPostProcessorBeanDefinitionRegistry,你可以动态地添加、修改或删除 Bean 定义。

下面是一个使用 BeanFactoryPostProcessorBeanDefinitionRegistry 来替换或增强 Bean 定义的例子:

public interface UserService {void performAction();  
}  
public class OriginalUserService implements UserService {@Override  public void performAction() {  System.out.println("Original UserService performing action.");  }  
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Configuration;public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;// 替换 OriginalUserService Bean  BeanDefinition originalUserServiceDef = registry.getBeanDefinition("originalUserService");if (originalUserServiceDef != null) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(EnhancedUserService.class);builder.addConstructorArgReference("originalUserService"); // 注入原始的 UserService BeanAbstractBeanDefinition beanDefinition = builder.getBeanDefinition();registry.registerBeanDefinition("enhancedUserService", beanDefinition);}}// 模拟增强的 UserService 实现  static class EnhancedUserService implements UserService {private final OriginalUserService originalUserService;public EnhancedUserService(OriginalUserService originalUserService) {this.originalUserService = originalUserService;}@Overridepublic void performAction() {System.out.println("Before enhanced action...");originalUserService.performAction();System.out.println("After enhanced action...");}}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic UserService originalUserService() {return new OriginalUserService();}@Beanpublic CustomBeanFactoryPostProcessor userServiceBeanFactoryPostProcessor() {return new CustomBeanFactoryPostProcessor();}
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.ApplicationContext;  public class Main {  public static void main(String[] args) {  ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  UserService originalUserService = context.getBean("enhancedUserService", UserService.class);originalUserService.performAction(); }
}

输出如下所示:

Before enhanced action...
Original UserService performing action.
After enhanced action...

通过上述步骤,我们展示了如何在 Spring 容器中使用 BeanFactoryPostProcessorBeanDefinitionRegistry 来动态地修改或增强 Bean 定义。这种方法特别适用于在不修改原始类代码的情况下,对应用进行扩展或修改。然而,在实际应用中,应谨慎使用这种方法,因为它可能会使应用的配置和依赖关系变得复杂和难以管理。

接下来解释加载方式的知识点:

选择懒加载的方式,那么直到我们伸手向Spring要依赖对象实例之前,其都是以BeanDefinationRegistry中的一个个的BeanDefination的形式存在,也就是Spring只有在我们需要依赖对象的时候才开启相应对象的实例化阶段;

而如果我们不是选择懒加载的方式,容器启动阶段完成之后,将立即启动Bean实例化阶段,通过隐式的调用所有依赖对象的getBean方法来实例化所有配置的Bean并保存起来。

1、对象创建策略:对象的创建采用了策略模式,BeanDefinationRegistry中的BeanDefination,我们可以使用反射的方式创建对象,也可以使用CGlib字节码生成创建对象。

2、BeanWrapper:由于Spring IOC容器为了统一对不同类型对象的访问,Spring给所有创建的Bean实例穿上了一层外套BeanWrapper。BeanWrapper实际上是对反射相关API的简单封装,我们要获取某个对象的属性、调用某个对象的方法,现在不需要在写繁杂的反射API了以及处理一堆麻烦的异常,直接通过BeanWrapper就可以完成相关操作。

在Spring框架中,BeanWrapper是一个非常重要的接口,它提供了对JavaBean属性的访问和修改的能力,同时也支持对JavaBean方法的调用。BeanWrapper是Spring IoC容器内部使用的核心组件之一,用于在Bean的实例化过程中设置Bean的属性值。以下是在Spring中BeanWrapper的用法:

1. 封装Bean实例

BeanWrapper是对JavaBean实例的封装,它允许Spring IoC容器以统一的方式访问和修改Bean的属性。当Spring IoC容器创建了一个Bean的实例后,它会使用BeanWrapper来封装这个实例,并通过BeanWrapper来设置Bean的属性值。

2. 访问和修改Bean属性

BeanWrapper提供了丰富的API来访问和修改Bean的属性。例如,可以使用getPropertyValue(String propertyName)方法来获取Bean的某个属性值,使用setPropertyValue(String propertyName, Object value)方法来设置Bean的某个属性值。此外,BeanWrapper还支持嵌套属性的访问和修改,例如可以通过"address.city"这样的属性名来访问和修改嵌套在address属性中的city属性。

3. 类型转换

在设置Bean属性值时,BeanWrapper会考虑属性值的类型转换。如果属性值的类型与Bean属性所需的类型不匹配,BeanWrapper会尝试使用注册在PropertyEditorRegistry中的属性编辑器(PropertyEditor)或ConversionService来进行类型转换。这使得开发者可以灵活地处理类型转换问题,而无需编写额外的代码。

4. 与Spring IoC容器的集成

BeanWrapper是Spring IoC容器内部使用的核心组件之一,它与Spring IoC容器的其他组件紧密集成。在Bean的实例化过程中,Spring IoC容器会创建BeanWrapper实例来封装Bean的实例,并通过BeanWrapper来设置Bean的属性值。此外,Spring IoC容器还利用BeanWrapper来支持依赖注入、AOP等功能。

5. 示例用法

虽然用户很少需要直接使用BeanWrapper进行编程,但了解其用法对于深入理解Spring IoC容器的内部机制非常有帮助。以下是一个简化的示例,展示了如何在Spring IoC容器外部使用BeanWrapper

package com.springIoc.BeanDefinitionDemo;import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;public class SpringBeanWrapperExample {public static void main(String[] args) {// 创建一个JavaBean实例Person person = new Person();// 创建BeanWrapperImpl实例来包装Person对象BeanWrapper personWrapper = new BeanWrapperImpl(person);// 使用BeanWrapper设置属性值personWrapper.setPropertyValue("name", "John Doe");personWrapper.setPropertyValue("age", 30);// 获取BeanWrapper封装的Bean实例并调用其方法(如果需要的话)Person modifiedPerson = (Person) personWrapper.getWrappedInstance();modifiedPerson.greet(); // 假设Person类有一个greet()方法}// 假设的Person类,与前面的示例相同// ...
}class Person {private String name;private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public void greet() {System.out.println("name = " + name);System.out.println("age = " + age);}
}

需要注意的是,上述示例仅用于演示BeanWrapper的用法,而在实际开发中,BeanWrapper的使用通常是由Spring IoC容器自动完成的,用户无需手动创建和使用BeanWrapper实例。

结论

BeanWrapper是Spring框架中一个非常重要的接口,它提供了对JavaBean属性的访问和修改的能力,同时也支持类型转换和与Spring IoC容器的集成。虽然用户很少需要直接使用BeanWrapper进行编程,但了解其用法对于深入理解Spring IoC容器的内部机制非常有帮助。

相关文章:

  • 【PostgreSQL】PostgreSQL 教程
  • 【python虚拟环境管理】【mac m3】 使用pipx安装poetry
  • ASP.NET第七章 --案例1
  • 关闭Ubuntu烦人的apport
  • rust编译安卓各个平台so库
  • 艺术与技术的交响曲:CSS绘图的艺术与实践
  • linux搭建mysql主从复制(一主一从)
  • Autosar RTE配置-Assembly和Delegation的使用-基于ETAS软件
  • Collections.unmodifiableList
  • 【Vue】Vue3 安装 Tailwind CSS 入门
  • Golang | Leetcode Golang题解之第240题搜索二维矩阵II
  • vue2导入elementui组件库
  • LabVIEW电路产品功能自动检测系统
  • 目标检测入门:4.目标检测中的一阶段模型和两阶段模型
  • 【ELK+Kafka+filebeat分布式日志收集】分布式日志收集详解
  • 【mysql】环境安装、服务启动、密码设置
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • Javascript基础之Array数组API
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • PHP那些事儿
  • 记录一下第一次使用npm
  • 聊聊directory traversal attack
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 新版博客前端前瞻
  • #Linux(Source Insight安装及工程建立)
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • ( 10 )MySQL中的外键
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (7)摄像机和云台
  • (8)STL算法之替换
  • (STM32笔记)九、RCC时钟树与时钟 第二部分
  • (纯JS)图片裁剪
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)mysql使用Navicat 导出和导入数据库
  • (自用)gtest单元测试
  • . NET自动找可写目录
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .NET Framework .NET Core与 .NET 的区别
  • .NET6实现破解Modbus poll点表配置文件
  • .Net程序帮助文档制作
  • .Net中wcf服务生成及调用
  • @WebServiceClient注解,wsdlLocation 可配置
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [ C++ ] STL---stack与queue
  • [Android]如何调试Native memory crash issue
  • [Android]一个简单使用Handler做Timer的例子
  • [Assignment] C++1
  • [C++内存管理]new,delete,operator new,opreator delete
  • [Day 43] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
  • [hdu 4405] Aeroplane chess [概率DP 期望]
  • [Hive] 常见函数
  • [IE编程] 如何编程清除IE缓存
  • [LeetCode]: 145: Binary Tree Postorder Traversal
  • [LeetCode]-使用特殊算法的题目-2