spring源码分析bean的生命周期(下)

news2024/11/15 8:36:41

 doGetBean()执行过程

createBean()执行过程


一、@DependsOn注解

spring创建对象之前会判断类上是否加了@DependsOn注解,加了会遍历然后会添加到一个map中,spring会先创建@DependsOn注解指定的类

二、spring类加载器

在合并BeanDefinition,确定beanName之后和scope之后就会调用createBean()方法获取类加载器,BeanDefinition合并之后,就可以去创建Bean对象了,而创建Bean就必须实例化对象,而实例化就必须先加载当前BeanDefinition所对应的class,在AbstractAutowireCapableBeanFactory类的createBean()方法中,一开始就会调用

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

这行代码就是去加载类,该方法是这么实现的:

if (mbd.hasBeanClass()) {
 return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
 return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
  doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
 }
else {
 return doResolveBeanClass(mbd, typesToMatch);
}
public boolean hasBeanClass() {
 return (this.beanClass instanceof Class);
}

如果beanClass属性的类型是Class,那么就直接返回,如果不是,则会根据类名进行加载(doResolveBeanClass方法所做的事情)

会利用BeanFactory所设置的类加载器来加载类,如果没有设置,则默认使用**ClassUtils.getDefaultClassLoader()**所返回的类加载器来加载。

ClassUtils.getDefaultClassLoader()

  1. 优先返回当前线程中的ClassLoader
  2. 线程中类加载器为null的情况下,返回ClassUtils类的类加载器
  3. 如果ClassUtils类的类加载器为空,那么则表示是Bootstrap类加载器加载的ClassUtils类,那么则返回系统类加载器

三、实例化前

当前BeanDefinition对应的类成功加载后,就可以实例化对象了,但是...

在Spring中,实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作。这个扩展点叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。比如:

@Component
public class TestBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("实例化前");
  }
  return null;
 }
}

如上代码会导致,在userService这个Bean实例化前,会进行打印。

值得注意的是,postProcessBeforeInstantiation()是有返回值的,如果这么实现:

@Component
public class TestBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("实例化前");
   return new UserService();
  }
  return null;
 }
}

userService这个Bean,在实例化前会直接返回一个由我们所定义的UserService对象。如果是这样,表示不需要Spring来实例化了,并且后续的Spring依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步。

四、实例化

在这个步骤中就会根据BeanDefinition去创建一个对象了。

1、Supplier创建对象

首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象

得直接使用BeanDefinition对象来设置Supplier,比如:

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {
 @Override
 public Object get() {
  return new UserService();
 }
});
context.registerBeanDefinition("userService", beanDefinition);

2、工厂方法创建对象

如果没有设置Supplier,则检查BeanDefinition中是否设置了factoryMethod,也就是工厂方法,有两种方式可以设置factoryMethod,比如:
方式一:

<bean id="userService" class="com.spring.service.UserService" factory-method="createUserService" />

对应的UserService类为:

public class UserService {

 public static UserService createUserService() {
  System.out.println("执行createUserService()");
  UserService userService = new UserService();
  return userService;
 }

 public void test() {
  System.out.println("test");
 }

}

方式二:

<bean id="commonService" class="com.spring.service.CommonService"/>
<bean id="userService1" factory-bean="commonService" factory-method="createUserService" />

对应的CommonService的类为:

public class CommonService {

 public UserService createUserService() {
  return new UserService();
 }
}

 Spring发现当前BeanDefinition方法设置了工厂方法后,就会区分这两种方式,然后调用工厂方法得到对象。

值得注意的是,我们通过@Bean所定义的BeanDefinition,是存在factoryMethod和factoryBean的,也就是和上面的方式二非常类似,@Bean所注解的方法就是factoryMethod,AppConfig对象就是factoryBean。如果@Bean所所注解的方法是static的,那么对应的就是方式一。

五、BeanDefinition后置处理器

Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以对此时的BeanDefinition进行加工,比如:

@Component
public class TestMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

 @Override
 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
  if ("userService".equals(beanName)) {
   beanDefinition.getPropertyValues().add("orderService", new OrderService());
  }
 }
}

 在Spring源码中,AutowiredAnnotationBeanPostProcessor就是一个MergedBeanDefinitionPostProcessor,它的postProcessMergedBeanDefinition()中会去查找注入点,并缓存在AutowiredAnnotationBeanPostProcessor对象的一个Map中(injectionMetadataCache)。

六、实例化后

在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),比如:

@Component
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

  if ("userService".equals(beanName)) {
   UserService userService = (UserService) bean;
   userService.test();
  }

  return true;
 }
}

七、自动注入

spring会根据配置@Bean的autowire是byType或者byName进行注入,另外spring会调用InstantiationAwareBeanPostProcessor.postProcessProperties()解析@Autowired注解

八、处理属性

这个步骤中,就会处理@Autowired、@Resource、@Value等注解,也是通过**InstantiationAwareBeanPostProcessor.postProcessProperties()**扩展点来实现的,比如我们甚至可以实现一个自己的自动注入功能,比如:

@Component
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   for (Field field : bean.getClass().getFields()) {
    if (field.isAnnotationPresent(TestInject.class)) {
     field.setAccessible(true);
     try {
      field.set(bean, "123");
     } catch (IllegalAccessException e) {
      e.printStackTrace();
     }
    }
   }
  }

  return pvs;
 }
}

九、执行Aware

完成了属性赋值之后,Spring会执行一些回调,包括:

  1. BeanNameAware:回传beanName给bean对象。
  2. BeanClassLoaderAware:回传classLoader给bean对象。
  3. BeanFactoryAware:回传beanFactory给对象。

十、初始化前

初始化前,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessBeforeInitialization(),比如

@Component
public class TestBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化前");
  }

  return bean;
 }
}

利用初始化前,可以对进行了依赖注入的Bean进行处理。

在Spring源码中:

  1. InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的方法,
  2. ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:
    1. EnvironmentAware:回传环境变量
    2. EmbeddedValueResolverAware:回传占位符解析器
    3. ResourceLoaderAware:回传资源加载器
    4. ApplicationEventPublisherAware:回传事件发布器
    5. MessageSourceAware:回传国际化资源
    6. ApplicationStartupAware:回传应用其他监听对象,可忽略
    7. ApplicationContextAware:回传Spring容器ApplicationContext

十一、初始化

  1. 查看当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()方法
  2. 执行BeanDefinition中指定的初始化方法

十二、、初始化后

这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization(),比如:

@Component
public class TestBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化后");
  }

  return bean;
 }
}

 可以在这个步骤中,对Bean最终进行处理,Spring中的AOP就是基于初始化后实现的,初始化后返回的对象才是最终的Bean对象

 总结BeanPostProcessor

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. 实例化
  3. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
  4. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  5. 自动注入
  6. InstantiationAwareBeanPostProcessor.postProcessProperties()
  7. Aware对象
  8. BeanPostProcessor.postProcessBeforeInitialization()
  9. 初始化
  10. BeanPostProcessor.postProcessAfterInitialization()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/900596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7.9和redhat6.9 离线升级OpenSSH和openssl (2023年的版本)

升级注意事项&#xff01; 1、多开几个连接窗口&#xff08;xshell&#xff09;&#xff0c;避免升级openssh失败无法再次连接终端&#xff0c;否则要跑机房了。 2、可开启telnet服务、vnc服务、打快照。多几个“保命”的路数。一、centos7.9的信息 [rootnode2 ~]# openssl v…

1391. 检查网格中是否存在有效路径;2502. 设计内存分配器;1638. 统计只差一个字符的子串数目

核心思想&#xff1a;并查集。枚举网格中的块&#xff0c;把能连通的连通在一起&#xff0c;最后看&#xff08;0&#xff0c;0&#xff09;和&#xff08;m-1,n-1&#xff09;是否连通&#xff0c;然后网格中的每个点坐标是二维的&#xff0c;然后通过x*ny转换为一维&#xff…

大数据课程K2——Spark的RDD弹性分布式数据集

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的RDD结构; ⚪ 掌握Spark的RDD操作方法; ⚪ 掌握Spark的RDD常用变换方法、常用执行方法; 一、Spark最核心的数据结构——RDD弹性分布式数据集 1. 概述 初学Spark时,把RDD看…

超实用的批量管理工具 pssh 和 window 文件传输工具 pscp

文章目录 一、概述1&#xff09;pssh2&#xff09;pscp 二、pssh 工具安装三、pssh 命令的基本语法四、pscp 工具安装1&#xff09;Windows 上安装2&#xff09;Linux 系统上安装 五、 pscp 命令的基本语法1&#xff09;从 windows 向 linux 传文件2&#xff09;从 linux 传文件…

算法:滑动窗口解决连续区间子数组问题

文章目录 实现原理实现思路典型例题长度最小的子数组无重复字符的最小字串最大连续1的个数III将x减到0的最小操作水果成篮找到字符串中所有字母异位词(哈希表比较优化)对哈希表内元素比较的优化 总结 本篇积累的是滑动窗口的问题&#xff0c;滑动窗口在算法实现中有重要作用&am…

Python可视化在量化交易中的应用(16)_Seaborn热力图

Seaborn中热力图的绘制方法 seaborn中绘制热力图使用的是sns.heatmap()函数&#xff1a; sns.heatmap(data,vmin,vmax,cmap,center,robust,annot,fmt‘.2g’,annot_kws,linewidths0,linecolor‘white’,cbar,cbar_kws,cbar_ax,square,xticklabels‘auto’,yticklabels‘auto’…

systemd:初学者如何理解其中的争议

导读对于什么是 systemd&#xff0c;以及为什么它经常成为 Linux 世界争议的焦点&#xff0c;你可能仍然感到困惑。我将尝试用简单的语言来回答。 在 Linux 世界中&#xff0c;很少有争议能像传统的 System V 初始化 系统&#xff08;通常称为 SysVinit&#xff09;和较新的 s…

QT设置widget背景图片

首先说方法&#xff0c;在给widget或者frame或者其他任何类型的控件添加背景图时&#xff0c;在样式表中加入如下代码&#xff0c;指定某个控件&#xff0c;设置其背景。 类名 # 控件名 { 填充方式&#xff1a;图片路径 } 例如&#xff1a; QWidget#Widget {border-image: url…

1. 微信小程序开发环境搭建

下载 微信的小程序开发需要使用到微信开发者工具&#xff0c;通过https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html可以下载 下载完成后 安装

Linux 系统编程拾遗

Linux 系统编程拾遗 进程的创建 进程的创建 fork()、exit()、wait()以及execve()的简介 创建新进程&#xff1a;fork()

人工智能原理(6)

目录 一、机器学习概述 1、学习和机器学习 2、学习系统 3、机器学习发展简史 4、机器学习分类 二、归纳学习 1、归纳学习的基本概念 2、变型空间学习 3、归纳偏置 三、决策树 1、决策树组成 2、决策树的构造算法CLS 3、ID3 4、决策树的偏置 四、基于实例的学习…

嵌入式系统总线-片内总线

1.总线概述 总线是CPU与存储器和设备通信的机制&#xff0c;是计算机各部件之间传送数据、地址和控制信息的公共通道。 2.总线参数 总线宽度&#xff1a;又称总线位宽&#xff0c;指的是总线能同时传送数据的位数。如16位总线就是具有16位数据传送能力。 总线频率&#xff…

apex安装出错:TypeError unsupported operand type(s) for +: “NoneType“ and “str“

Windows 10 环境下安装apex报错&#xff1a;TypeError unsupported operand type(s) for : “NoneType“ and “str“ 1、首先apex不能直接pip install apex安装。 2、具体安装步骤&#xff1a;【python】【深度学习】apex的安装_apex python_愿东大没有食堂的博客-CSDN博客 …

深入竞品:解读竞品分析的艺术与策略

引言&#xff1a;为何竞品分析至关重要&#xff1f; 在当今的产品环境中&#xff0c;市场变得越来越拥挤。每个角落都有新的创业公司试图创造下一个行业的颠覆者&#xff0c;同时也有成熟的巨头在不断地迭代和优化他们的产品。在这样的环境中&#xff0c;不了解您的竞争对手是…

『C语言初阶』第八章 -结构体

前言 今天小羊又来给铁汁们分享关于C语言的结构体&#xff0c;在C语言中&#xff0c;结构体类型属于一种构造类型&#xff08;其他的构造类型还有&#xff1a;数组类型&#xff0c;联合类型&#xff09;&#xff0c;今天我们主要简单了解一下结构体。 一、结构体是什么&#x…

Linux Mint 21.3 计划于 2023 年圣诞节发布

Linux Mint 项目近日公布了基于 Ubuntu 的 Linux Mint 发行版下一个重要版本的一些初步细节&#xff0c;以及备受期待的基于 Debian 的 LMDE 6&#xff08;Linux Mint Debian Edition&#xff09;版本。 近日&#xff0c;Linux Mint 项目负责人克莱门特-勒菲弗&#xff08;Clem…

ECA模块详解

注意&#xff1a;本文代码为自己理解之后实现&#xff0c;与原论文代码原理相同但并不完全一样&#xff0c;主要是输入张量的形状不同&#xff0c;若更想了解原文代码&#xff0c;可以访问&#xff1a;https://blog.csdn.net/weixin_45084253/article/details/124282580 &#…

使用RDP可视化远程桌面连接Linux系统

使用RDP可视化远程桌面连接Linux系统 远程桌面连接Linux安装安装包准备服务器安装xrdp远程连接 远程桌面连接Linux 通常使用SSH来连接服务器&#xff0c;进行命令行操作&#xff0c;但是这次需要远程调试生产环境的内网服务器&#xff0c;进行浏览器访问内网网站&#xff0c;至…

SQL助你面大厂(行列转换)

在面试中,不仅有算法题,还有这个老大难的SQL编写 SQL在面试中也是会经常会被问到&#xff0c;不仅仅是为了面试&#xff0c;在做项目的时候&#xff0c;往往用的最多的就是CRUD,这也提高不了我们的编写SQL的能力&#xff0c;所以最近我准备总结几个面试模板&#xff0c;以及一些…