Spring中Bean的实例化详细流程

news/2024/10/18 7:58:31/

还是举个例子,我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩,但是呢我这个朋友家里搞的很乱,所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了,然后给他介绍了一个保姆大S(PS:本文无意指向任何人,因为Spring的前缀是S)然后就把家里整理得井井有条,就请朋友来家里玩了。

好了引入正文,很早很早的Java开发者应该熟悉,最早的时候我们前端访问后端都是需要自己写Servlet的,大概是一个接口写一个Servlet。Java开发又是面向对象的编程,我们程序里面写了new 了很多的对象。写了很多个Servlet,对象很难管理造成我们的程序很乱,都看不下去。后面Spring来了对象都交给了Spring管理,Servlet相关的也都交给了SpringMVC,这样我们开发就顺利多了。好了这下懂我上面举的例子了吧,懂得保姆是什么意思了吧【Spring就像一个管家,一个保姆】。所以多了解Spring相关知识我们提高开发效率有很大的帮助。既然我们的对象交给了Spring管理,那我们的对象怎么生成的呢,就让我们一起看下。

我们在使用Spring的时候,容器中的Bean在我们项目启动的时候都已经给我们生成了,直接使用就行了。容器启动的时候会调用这个方法:

AbstractApplicationContext.refresh()

然后就会调用下面这个方法:

// Instantiate all remaining (non-lazy-init) singletons.// 翻译一下就是 实例化所有非懒加载的Bean
finishBeanFactoryInitialization(beanFactory);

如图refresh中的方法,它再次调用的每个方法都很重要,实例化所有单例Bean的方法在这个方法的最后调用

我们写的对象基本都在这个方法内进行实例化。【PS方法只讲一些很重要的,具体的更详细方法调用我会在文章后面的流程图中展示出来。】

DefaultListableBeanFactory.preInstantiateSingletons()。
@Overridepublic void preInstantiateSingletons() throws BeansException {// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.// 获取所有的要实例化的Bean的名称List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...// 开始初始化单例的Beanfor (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// Bean 不是抽象的,是单例的,不是懒加载的进入如下分支if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 如果是FactoryBean进入此分支。本次只聊自己开发写的非FactoryBean// 所以聊else下面的分支。if (isFactoryBean(beanName)) {// FactoryBean的名称很特别Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {final FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {// 非 FactoryBean进入此分支getBean(beanName);}}}}

然后会进入如下方法。

AbstractBeanFactory.doGetBean() 的方法。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {//  这个方法主要是获取Bean的名称,一些Bean的名称可能命名的比较特别// 需要进行转换。final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.// 首先先从容器的缓存中获取Bean,如果容器中已经存在,直接返回。Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);} else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.// 先检查这个Bean是否在创建中,如果在创建中抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 标记Bean为正在创建中。if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {// Create bean instance.//  如果Bean是单例开始创建Bean .//  后面判断还有Prototype(多例)不是要讲的重点,代码删除了。if (mbd.isSingleton()) {//  这个方法是Java 8的 lambda 写法,这个方法里面会把创建好的// Bean放到Spring容器中,后面再获取这个Bean直接从容器中获取了。sharedInstance = getSingleton(beanName, () -> {try {// 正式开始创建Bean 。return createBean(beanName, mbd, args);}catch (BeansException ex) {// 创建过程出现异常,销毁BeandestroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}return (T) bean;}

然后是正式真正的创建Bean的方法如下:

AbstractAutowireCapableBeanFactory.createBean() 的方法。
@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {RootBeanDefinition mbdToUse = mbd;try {// doCreateBean 是Spring正在做事的方法。Object beanInstance = doCreateBean(beanName, mbdToUse, args);}protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.//  实例化Bean 先创建一个BeanWrapper .这个方法里面Spring 一般为默认// 无参的构造方法创建对象,所以大家如果重写对象的构造方法的时候,一定// 要把无参构造方法也写出来。要不然某些情况下启动Spring容器可能会报错。if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}// Initialize the bean instance.Object exposedObject = bean;try {// 为Bean的属性赋值。populateBean(beanName, mbd, instanceWrapper);// 初始化Bean 。exposedObject = initializeBean(beanName, exposedObject, mbd);}return exposedObject;}// 初始化Bean。protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 如果你的Bean实现了Spring内置的Aware方法,会在这里执行invokeAwareMethods(beanName, bean);Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// 执行Bean的初始化前置处理器,很重要也就是Spring的钩子函数wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 执行Bean的初始化方法invokeInitMethods(beanName, wrappedBean, mbd);}if (mbd == null || !mbd.isSynthetic()) {// 执行Bean的后置处理器,也很重要。// 很多写底层架构的人都会对此钩子方法灵活应用wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}

PS:下面是Bean实例化的详细的流程图,由于画好后的整个流程图无法完全保存,只有一张一张的截屏了。图片一张一张往下看就是整个完整的流程,自己可以找着图片一步一步看,就会对Bean的整个流程很清楚了。

读完熟悉了Spring实例化的流程你能做些什么呢?

1:比如实现BeanPostProcessor。A初始化前和后分别会执行下面2个方法。

@Component
public class A implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName);return null;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName);return null;}
}

2:实现InitializingBeanA初始化的时候会执行以下方法。

@Component
public class A implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("执行InitializingBean的afterPropertiesSet方法");}
}

上面实现的方法都会在A实例化的时候执行,如果你写的业务逻辑有需要在A实例化时候执行的就可以使用上面的方法完成。

欢迎跟着图中走一遍源码,相信你会对Spring创建Bean的流程更加了解。看一些源码你的思路会更清晰,写代码也更得心应手,写代码的时候你可能不自己觉的就按照这些大神写代码的思路去完成高质量的代码。


http://www.ppmy.cn/news/998192.html

相关文章

“深入理解Spring Boot:构建独立、可扩展的企业级应用程序的最佳实践“

标题&#xff1a;深入理解Spring Boot&#xff1a;构建独立、可扩展的企业级应用程序的最佳实践 摘要&#xff1a;Spring Boot是一个强大的框架&#xff0c;可以帮助开发人员快速构建独立、可扩展的企业级应用程序。本文将深入探讨Spring Boot的核心概念和最佳实践&#xff0c…

MultipartFile 获取文件名、文件前缀、文件后缀、文件类型

测试 debug 方法 RequestMapping(value "/test",method RequestMethod.POST)public void fileUpload(MultipartFile file){// 文件名String originalFilename file.getOriginalFilename();// 文件名前缀String fileName file.getOriginalFilename().substring(0,…

【WebRTC---源码篇】(二:一)PeerConnection详解

Track的添加 上图是整体流程图 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::AddTrack(rtc::scoped_refptr<MediaStreamTrackInterface> track,const std::vector<std::string>& stream_ids) {RTC_DCHECK_RUN_ON(signal…

ORACLE冷备份及恢复

备份 直接拷贝oracle目录下的admin、oradata(datafile&#xff0c; controlfile&#xff0c;redo)、flash_recovery_area三个文件夹&#xff0c;db_1目录下database(PWDfile、pfile)、dbs(spfile)、NETWORK/ADMIN(listener.ora、tnsnames.ora)&#xff0c;到其他存储实现备份。…

html中非插件实现pdf预览【PC+H5】

这里只考虑非插件以外的方法&#xff0c;插件可以参考pdf.js 1. iframe标签 <iframe src"/file/read?fileaaa&typeupload" width"100%" height"800px"></iframe>经测试&#xff0c;chrome正常显示&#xff0c;firefox不能显示…

HBase概述

HBase 一 HBase简介与环境部署 1.1 HBase简介&在Hadoop生态中的地位 1.1.1 什么是HBase HBase是一个分布式的、面向列的开源数据库HBase是Google BigTable的开源实现HBase不同于一般的关系数据库, 适合非结构化数据存储 1.1.2 BigTable BigTable是Google设计的分布式…

EPICS通道访问练习2--ca_array_get_callback的使用

编写以下程序的目的是测试ca_array_get_callback通道访问库函数的用法以及如何传递用户参数给与之设置的回调函数。 1&#xff09; struct ca_connection_handler结构体在连接状态变化时作为参数传递给设定的回调函数&#xff1b;caCh是回调函数的原型&#xff0c;即在定义连接…

使用Qt中的QDir类进行目录操作

文章目录 概述QDir类的基本功能获取当前目录创建目录列出目录内容筛选目录内容筛选特定命名文件 复制文件和目录删除文件和目录 应用场景总结 概述 Qt是一个跨平台的C应用程序开发框架&#xff0c;其中提供了许多方便的类来处理文件和目录操作。其中&#xff0c;QDir类是用于处…