Spring-生命周期解析

Bean的生命周期

Bean的生成过程有如下几个步骤:

第一步:生成BeanDefinition

spring在启动并进行准备工作之后,会执行扫描过程,扫描过程如下:

  1. 先扫描包路径下所有的class文件对象得到Resource对象

  1. 利用ASM技术解析class文件对象,Resource对象得到MetadataReader

  1. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选,筛选通过之后,得到ScannedGenericBeanDefinition

  1. 判断当前扫描的类是否是抽象类或接口等判断

  1. 如果不是顶级类、或静态内部类,则不通过

  1. 如果是抽象类或接口,则不通过

  1. 如果不是抽象类,但是有@Lookup注解的方法则不通过

  1. 最终扫描得到BeanDefinition

  1. 解析@Lazy、@Primary、@DependsOn、@Role、@Description等注解并赋值给BeanDefinition对应的属性

  1. 将BeanDefinition放入缓存beanDefinitionMap

  1. 如果已经存在,则报错

第二步:合并BeanDefinition

通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在Spring中支持父子BeanDefinition,子Bean未定义的属性可以继承父Bean已定义的属性。

使用示例::

<bean id="parent" class="com.ligangit.service.Parent" autowire="byType"/>
<bean id="child" class="com.ligangit.service.Child" parent="parent"/>

指定父Bean之后,child的自动注入方式也是byType。

因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。

而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。

第三步:加载类

在创建Bean必须实例化对象,也就必须先加载当前BeanDefinition所对应的class,在AbstractAutowireCapableBeanFactory类的createBean()方法中,一开始就会调用:

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

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

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

第四步:实例化前

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

@Component
public class MyBeanPostProcessor 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实例化前,会进行打印。

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

如果不需要变更注入的bean对象,直接返回null即可。

第五步:实例化

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

Supplier创建对象

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

工厂方法创建对象

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

方式一:

<bean id="userService" class="com.ligangit.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.ligangit.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的,那么对应的就是方式一。

推断构造方法

推断完构造方法后,就会使用构造方法来进行实例化了。

  1. 当定义了一个构造方法,那么就会直接用这个构造方法

  1. 如果此构造方法,是有参构造方法,则会找入参对象的bean作为入参。

  1. 如何找入参bean对象?

  1. 先根据入参的类型(OrderService.calss),找bean对象

  1. 如果bean对象只找到一个,则直接入参

  1. 如果bean对象找到多个,则再匹配形参名称,匹配到了则入参,否则报错,提示找到多个,但是没有匹配一个

  1. 如果未找到bean,则报错

  1. 当定义了多个构造方法,那么会判断是否有无参构造方法,

  1. 如果有,则使用无参构造方法

  1. 若无,则会报错,因为Spring不知道应该使用哪个构造方法

  1. 解决方式:可以在某个构造方法上加@Autowired注解,标记使用此构造方法

  1. 所以,多个构造方法时,会先判断某个构造方法上是否有@Autowired注解,如果有,则直接使用那个方法,否则再判断是否有无参构造方法

  1. 注意:如果配置了autowire=constructor,则让Spring自动去寻找bean做为构造方法参数值

额外的,在推断构造方法逻辑中除开会去选择构造方法以及查找入参对象意外,会还判断是否在对应的类中是否存在使用@Lookup注解了方法。如果存在则把该方法封装为LookupOverride对象并添加到BeanDefinition中。

在实例化时,如果判断出来当前BeanDefinition中没有LookupOverride,那就直接用构造方法反射得到一个实例对象。如果存在LookupOverride对象,也就是类中存在@Lookup注解了的方法,那就会生成一个代理对象。

第六步:BeanDefinition的后置处理

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

@Component
public class MyMergedBeanDefinitionPostProcessor 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的后置处理后,会缓存一份创建bean原始对象或代理对象的lambda表达式,放入第三级缓存singletonFactories中,便于处理后续循环依赖。

第七步:实例化后

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

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

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

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

  return true;
 }
}

上述代码就是对userService所实例化出来的对象进行处理。

这个扩展点,在Spring源码中基本没有怎么使用。

第八步:自动注入

这里的自动注入指的是Spring的自动注入。

对于@Autowired注解的注入:

Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点并缓存,在AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历所找到的注入点依次进行注入。

1. 遍历当前类的所有的属性字段Field
2. 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
3. 如果字段是static的,则不进行注入
4. 获取@Autowired中的required属性的值
5. 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。
6. 遍历当前类的所有方法Method
7. 判断当前Method是否是桥接方法,如果是找到原方法
8. 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
9. 如果方法是static的,则不进行注入
10. 获取@Autowired中的required属性的值
11. 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中。
12. 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。
13. 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存。

对于@Resource注解,则会使用CommonAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点并缓存,,在CommonAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历所找到的注入点依次进行注入。

第九步: 处理属性

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

@Component
public class MyInstantiationAwareBeanPostProcessor 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(MyInject.class)) {
     field.setAccessible(true);
     try {
      field.set(bean, "123");
     } catch (IllegalAccessException e) {
      e.printStackTrace();
     }
    }
   }
  }

  return pvs;
 }
}

第十步:执行Aware

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

  1. BeanNameAware:回传beanName给bean对象。

  1. BeanClassLoaderAware:回传classLoader给bean对象。

  1. BeanFactoryAware:回传beanFactory给对象。

第十一步:初始化前

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

@Component
public class MyBeanPostProcessor 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的方法,

  1. ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:

  1. EnvironmentAware:回传环境变量

  1. EmbeddedValueResolverAware:回传占位符解析器

  1. ResourceLoaderAware:回传资源加载器

  1. ApplicationEventPublisherAware:回传事件发布器

  1. MessageSourceAware:回传国际化资源

  1. ApplicationStartupAware:回传应用其他监听对象,可忽略

  1. ApplicationContextAware:回传Spring容器ApplicationContext

第十二步:初始化

  1. 查看当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()方法

  1. 执行BeanDefinition中指定的初始化方法

第十三步:初始化后

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

@Component
public class MyBeanPostProcessor 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对象

循环依赖

Spring中处理循环依赖就是利用三级缓存:

  • 一级缓存为:singletonObjects

缓存的是已经经历了完整生命周期的bean对象

  • 二级缓存为:earlySingletonObjects

比singletonObjects多了一个early,表示缓存的是早期的bean对象。早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。

  • 三级缓存为:singletonFactories

缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。

一些循环依赖场景:

  1. 构造方法导致的循环依赖:

会报错,因为原始对象都创建不出来

解决方式:在某个bean的构造方法上使用@Lazy注解

  1. 原型bean情况下的循环依赖:

会报错,每次都是新的对象,没有缓存,无法处理

没有解决方式

  1. @Async情况下的循环依赖:

看@Async用在哪个bean中,如果循环依赖中A先创建,B后创建,那么@Async在A中使用,会报错,但在B中不会报错

报错原因:@Async启用后,Spring会对容器中添加一个新的BeanPostProcessor,执行之后得到的bean代理对象和earlySingletonObjects中缓存的代理对象不是同一个,所以会报错

解决方式:在A中注入的B属性上添加@Lazy注解,让b懒加载

  1. @Transaction情况下的循环依赖:

可以正常使用,因为Spring会对容器中添加的是Advice,而不是BeanPostProcessor,不会变更bean代理对象

  1. BeanFactoryAware下的循环依赖:

即自己注入自己,这也是循环依赖:注入的时候先去singletonObjects中查找,然后在去earlySingletonObjects,再去singletonFactories中查找

可以正常使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值