Spring源码之Bean的生命周期

2023-11-18

        Spring已经成为了目前最流行的第三方开源框架之一,我们在充分享受Spring IOC容器带来的便捷时,也应该考虑一下Spring这个大工厂是如何将一个个的Bean生产出来的,我们一起来讨论一下Spring中Bean的生命周期。

        Spring容器就好像生产车间的流水线,创建出来的对象就像是生产出来的一个个精美绝伦的产品。 Spring将管理的对象称之为Bean。

        Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期。Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的。

      Bean的一生总体上来说可以分为三个阶段:

  • 容器启动阶段
  • Bean的生成
  • Bean的销毁

一、容器启动阶段

        容器的启动阶段做了很多的预热工作,为后面Bean的实例化做好了充分的准备。

        1. 容器的初始化

        以AnnotationConfigApplicationContext容器为例,我们看它的构造函数:

	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

        首先,它会初始化一个AnnotatedBeanDefinitionReader为BeanDefinition读取器,它可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,比如:

    @Test
    public void testAnnotatedBeanDefinitionReader() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
        AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);
        // 将Teacher.class解析为BeanDefinition
        annotatedBeanDefinitionReader.register(Teacher.class);
        System.out.println(applicationContext.getBean("teacher"));
    }

        另外,ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition,比如:

    @Test
    public void testClassPathBeanDefinitionScanner() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.refresh();
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(applicationContext);
        scanner.scan("cn.kieasar");
        System.out.println(applicationContext.getBean("goodsService"));
    }

        2、BeanDefination

        在Java世界中,万物皆对象,散落于程序代码各处的注解以及保存在磁盘上的xml或者其他文件等等配置元信息,在内存中总要以一种对象的形式表示,就好比我们活生生的人对应到Java世界中就是一个Person类。

        而Spring在内存中表示这些对象的方式就是BeanDefination,Bean对象被加载到内存之后是以BeanDefination的形式存在。

      3、BeanDefinationRegistry

        这样我们需要创建某一个对象实例的时候,找到相应的BeanDefination然后创建对象即可。那么我们需要某一个对象的时候,去哪里找到对应的BeanDefination呢?

        通过Bean定义的id找到对象的BeanDefination的对应关系或者说映射关系又是如何保存的呢?这就引出了BeanDefinationRegistry了。

        Spring通过BeanDefinationReader将对象加载到内存生成相应的BeanDefination之后,就将其注册到BeanDefinationRegistry中,BeanDefinationRegistry就是一个存放BeanDefination的大篮子,它也是一种键值对的形式,通过特定的Bean定义的id,映射到相应的BeanDefination。

        4、BeanFactoryPostProcessor

        BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中的一个个的BeanDefination进行一定程度上的修改与替换。

        比如通过以下代码定义一个BeanPostProcessor: 

@Component
public class KieasarBeanPostProcessor implements BeanPostProcessor {

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

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

        一个BeanPostProcessor可以在任意一个Bean的初始化之前和初始化之后去做一些自定义的逻辑,当然,我们可以通过判断beanName来进行针对性处理(针对某个Bean,或某部分
Bean),通过定义BeanPostProcessor可以干涉Spring创建Bean的过程。

        例如配置Jdbc的DataSource连接的时候可以这样配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
    <property name="maxIdle" value="${jdbc.maxIdle}"></property>  
    <property name="maxActive" value="${jdbc.maxActive}"></property>  
    <property name="maxWait" value="${jdbc.maxWait}"></property>  
    <property name="minIdle" value="${jdbc.minIdle}"></property>  
    <property name="driverClassName" value="${jdbc.driverClassName}"></property>  
    <property name="url" value="${jdbc.url}"></property>  
    <property name="username" value="${jdbc.username}"></property>  
    <property name="password" value="${jdbc.password}"></property>  
</bean>

        BeanFactoryPostProcessor就会对注册到BeanDefinationRegistry中的BeanDefination做最后的修改,替换$占位符为配置文件中的真实的数据。

        至此,整个容器启动阶段就算完成了,容器的启动阶段的最终产物就是注册到BeanDefinationRegistry中的一个个BeanDefination了,这就是Spring为Bean实例化所做的预热的工作。让我们再通过一张图简单回顾一下:

二、Bean的生成

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

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

        1. 生成BeanDefinition

        Spring启动的时候会进行扫描,会先调用org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan()方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// 根据扫描路径,得到BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			// 解析class
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@lazy、@Primary、@DependsOn、@Role、@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 检查Spring容器是否已经存在beanName
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 注册
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

扫描某个包路径,并得到BeanDefinition的Set集合。

扫描流程为:

  1. 通过ResourcePatternResolver获得指定包路径下的所有 .class 文件(Spring源码中将此文件包装成了Resource对象);
  2. 遍历每个Resource对象;
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader);
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。);
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition;
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类;
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集。

        上面是说的通过扫描得到BeanDefinition对象,还可以通过直接定义BeanDefinition,或
解析spring.xml文件的<bean/>,或者@Bean注解得到BeanDefinition对象。

        2. 合并BeanDefinition

        通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在
Spring中支持父子BeanDefinition。

        父子BeanDefinition实际用的比较少,使用是这样的,比如:

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child"/>

        这种情况下,child是单例Bean。

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

        这种情况下,child就是原型Bean了。 因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。 
        而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的
BeanDefinition(后面Bean的初始化源码中会提到)。

        3. 加载类

        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);
}

        进入hasBeanClass()方法:

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类,则返回系统类加载器。

        4. 实例化前

        当前BeanDefinition对应的类成功加载后,就可以实例化对象了,但是在Spring中,实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作。这个扩展点叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。比如: 

@Component
public class IBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    /**
     * 这个方法很重要,在spring中就是在这个方法里面处理@Autowired、@Resource、@Value注解
     * @param pvs
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return null;
    }

    /**
     * 实例化之前调用的方法,传的参数不是对象(先看这个方法)
     */
    @SneakyThrows
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("userService")) {
            System.out.println("实例化前");
        }
        return null;
    }
}

        在userService这个Bean实例化前,会进行打印。 
        值得注意的是,postProcessBeforeInstantiation()方法是有返回值的,如果按上面代码返回null,表示不需要Spring来实例化了,并且后续的Spring依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步。

       5. 实例化

        在这里就会根据BeanDefinition去创建对象了。会调用org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons()方法,先大致看一下:

	// 实例化方法
	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// 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.
		// 前面的registerBeanDefinition()方法会把所有的beanName缓存到这里
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// 在spring容器初始化时,遍历这个beanDefinitionNames,从这里拿beanDefinition的name
		for (String beanName : beanNames) {
			// 先合并BeanDefinition(如果存在parent,则合并父子BeanDefinition的属性,生成一个RootBeanDefinition)
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 如果是单例的、非抽象的、非懒加载的就实例化
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				// 判断bean是否实现FactoryBean接口
				if (isFactoryBean(beanName)) {
					// 获取FactoryBean对象,创建完之后放到单例池
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(
									(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							// SmartFactoryBean继承FactoryBean接口,有个isEagerInit()方法设置是否容器启动就创建getObject()的实例
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							// 创建真正的Bean对象(调用getObject()返回的对象)
							getBean(beanName);
						}
					}
				}
				else {
					// 再实例化(创建Bean对象)******
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		// 所有的非懒加载单例Bean都创建完之后
		for (String beanName : beanNames) {
			// 从单例池中拿beanName对应的单例Bean
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					// 所有的单例Bean都创建完之后,才会调用每个单例Bean的该方法
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

        源码的详细解读会在后面的章节进行,本章先让大家有个初步的概念。

        6. BeanDefinition的后置处理

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

/**
 * @ClassName: MBeanPostProcessor
 * @Description: MergedBeanDefinitionPostProcessor接口的使用,会在Bean实例化时调用postProcessMergedBeanDefinition()方法
 * @Author: Kaisheng Du
 * @Date: 2021/9/25 10:35
 * @Version: 1.0
 */
@Component
public class MBeanPostProcessor implements MergedBeanDefinitionPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    /**
     * 实例化时调用的方法
     */
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanName.equals("userService")) {
            // 指定BeanDefinition初始化时调用的方法(eg:调用test()方法)
            beanDefinition.setInitMethodName("test");
            // 在这里可以对BeanDefinition的属性进行赋值
            beanDefinition.getPropertyValues().add("student",new Student());
            // 指定销毁时执行的方法
            beanDefinition.setDestroyMethodName("close");
        }
    }
}

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

        7.属性填充

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

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

关于@Autowired、@Resource、@Value的底层源码,会在后续的依赖注入过程中详解。

        8. 执行Aware

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

  • BeanNameAware:回传beanName给bean对象;
  • BeanClassLoaderAware:回传classLoader给bean对象;
  • BeanFactoryAware:回传beanFactory给对象。

        这一步的实现是先检查相关的Aware接口,然后去Spring的对象池(也就是容器,也就是那个Map结构)中去查找相关的实例(例如对于ApplicationContextAware接口,就去找ApplicationContext实例),也就是说我们必须要在配置文件中或者使用注解的方式,将相关实例注册容器中,BeanFactory才可以为我们自动注入。

        而对于ApplicationContext,由于其本身继承了一系列的相关接口,所以当检测到Aware相关接口,需要相关依赖对象的时候,ApplicationContext完全可以将自身注入到其中,ApplicationContext实现这一步是通过下面要讲到的东东——BeanPostProcessor:

        例如,ApplicationContext继承自ResourceLoader和MessageSource,那么当我们实现ResourceLoaderAware和MessageSourceAware相关接口时,就将其自身注入到业务对象中即可。 

        9. 初始化前(BeanPostProcessor前置处理)

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

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

        BeanFactoryPostProcessor存在于容器启动阶段,而BeanPostProcessor存在于对象实例化阶段,BeanFactoryPostProcessor关注对象被创建之前那些配置的修修改改、缝缝补补,而BeanPostProcessor阶段关注对象已经被创建之后的功能增强,替换等操作,这样就很容易区分了。

        BeanPostProcessor与BeanFactoryPostProcessor都是Spring在Bean生产过程中强有力的扩展点。如果你还对它感到很陌生,那么你肯定知道Spring中著名的AOP(面向切面编程),其实就是依赖BeanPostProcessor对Bean对象功能增强的。

        BeanPostProcessor前置处理就是在要生产的Bean实例放到容器之前,允许我们程序员对Bean实例进行一定程度的修改、替换等操作。

        前面讲到的ApplicationContext对于Aware接口的检查与自动注入就是通过BeanPostProcessor实现的,在这一步Spring将检查Bean中是否实现了相关的Aware接口,如果是的话,那么就将其自身注入Bean中即可。Spring AOP就是在这一步实现的偷梁换柱,产生对于原生对象的代理对象,然后将对源对象上的方法调用,转而使用代理对象的相同方法调用实现的。

        利用初始化前,可以对进行了依赖注入的Bean进行处理:
        1. InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的
方法,
        2. ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:
        i. EnvironmentAware:回传环境变量
        ii. EmbeddedValueResolverAware:回传占位符解析器
        iii. ResourceLoaderAware:回传资源加载器
        iv. ApplicationEventPublisherAware:回传事件发布器
        v. MessageSourceAware:回传国际化资源
        vi. ApplicationStartupAware:回传应用其他监听对象,可忽略
        vii. ApplicationContextAware:回传Spring容器ApplicationContext

        10. 初始化

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

        11.初始化后(BeanPostProcess后置处理)

        与前置处理类似,这里是在Bean自定义逻辑也执行完成之后,Spring又留给我们的最后一个扩展点。我们可以在这里在做一些我们想要的扩展。BeanPostProcessor.postProcessAfterInitialization(),比如:

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

三、Bean的销毁

        Bean销毁是在Spring容器关闭过程中进行的。例如:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
applicationContext.close();

        在Bean创建过程中,在最后(初始化之后),有一个步骤会去判断当前创建的Bean是不是
DisposableBean:

  1.  当前Bean是否实现了DisposableBean接口或当前Bean是否实现了AutoCloseable接口
  2. BeanDefinition中是否指定了destroyMethod
  3. 调用DestructionAwareBeanPostProcessor.requiresDestruction(bean)进行判断i. ApplicationListenerDetector中直接使得ApplicationListener是DisposableBeanii. InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是DisposableBean
  4. 把符合上述任意一个条件的Bean适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)   

        在 bean 创 建 完 成 后 就 会 对 这 个 bean 注 册 一 个 销 毁 的 Adapter 对 象 ,在doGetBean()中的registerDisposableBeanIfNecessary() 方法中 disposableBeans 集合负责对需要销毁的 bean 进行放置。

所属类:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

 /**
       * TODO Bean实例化+IOC依赖注入完成之后的调用,重要程度:5
       *     init-method属性和initializingBean接口方法afterPropertiesSet()调用,@PostConstruct注解方法调用,以及AOP代理对象的生成入口
       */
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }

   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }

   // Register bean as disposable.
   try {
      // 注册bean销毁时的类DisposableBeanAdapter
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

        registerDisposableBeanIfNecessary() 方法用来注册bean销毁时的类DisposableBeanAdapter,注册执行给定bean的所有销毁工作的DisposableBean实现:DestructionAwareBeanPostProcessors、DisposableBean接口、自定义销毁方法。

        DisposableBeanAdapter 对象就是负责 bean 销毁的类,这个类中收集 bean 是否实现了 DisposableBean 接口。

        其实对象的销毁与Spring关系不大,跟JVM有关,只是Spring在bean销毁前做一些事情,比如在容器销毁的时候释放一些资源等。

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
   AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
   if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
      if (mbd.isSingleton()) {
         // Register a DisposableBean implementation that performs all destruction
         // work for the given bean: DestructionAwareBeanPostProcessors,
         // DisposableBean interface, custom destroy method.
         // 注册执行给定bean的所有销毁工作的DisposableBean实现:DestructionAwareBeanPostProcessors、DisposableBean接口、自定义销毁方法。
         // 建立映射关系(beanName和DisposableBeanAdapter),注册bean销毁的类DisposableBeanAdapter,
         // 进入DisposableBeanAdapter-->
         registerDisposableBean(beanName, new DisposableBeanAdapter(
               // 重点看getBeanPostProcessorCache().destructionAware,BeanPostProcessor的应用
               bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
      }
      else {
         // A bean with a custom scope...
         Scope scope = this.scopes.get(mbd.getScope());
         if (scope == null) {
            throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
         }
         scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
               bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
      }
   }
}

进入registerDisposableBean() 方法,每一次bean的实例化在完成之后调用这个方法。

public void registerDisposableBean(String beanName, DisposableBean bean) {
   synchronized (this.disposableBeans) {
      // 建立映射关系
      this.disposableBeans.put(beanName, bean);
   }
}

进入DisposableBeanAdapter类的构造方法:

public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
      List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {

   Assert.notNull(bean, "Disposable bean must not be null");
   this.bean = bean;
   this.beanName = beanName;
   this.invokeDisposableBean =
         // 看当前bean是否实现了DisposableBean接口
         (this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
   this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
   this.acc = acc;
   String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
   if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
         !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
      this.destroyMethodName = destroyMethodName;
      Method destroyMethod = determineDestroyMethod(destroyMethodName);
      if (destroyMethod == null) {
         if (beanDefinition.isEnforceDestroyMethod()) {
            throw new BeanDefinitionValidationException("Could not find a destroy method named '" +
                  destroyMethodName + "' on bean with name '" + beanName + "'");
         }
      }
      else {
         Class<?>[] paramTypes = destroyMethod.getParameterTypes();
         if (paramTypes.length > 1) {
            throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
                  beanName + "' has more than one parameter - not supported as destroy method");
         }
         else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
            throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
                  beanName + "' has a non-boolean parameter - not supported as destroy method");
         }
         destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
      }
      this.destroyMethod = destroyMethod;
   }
   // 看filterPostProcessors()方法
   this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
}

进入filterPostProcessors() 方法:

private List<DestructionAwareBeanPostProcessor> filterPostProcessors(
      List<DestructionAwareBeanPostProcessor> processors, Object bean) {

   List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
   if (!CollectionUtils.isEmpty(processors)) {
      filteredPostProcessors = new ArrayList<>(processors.size());
      for (DestructionAwareBeanPostProcessor processor : processors) {
         if (processor.requiresDestruction(bean)) {
            // 过滤的PostProcessor就是CommonAnnotationBeanPostProcessor,因为这个类处理@PreDestroy注解
            filteredPostProcessors.add(processor);
         }
      }
   }
   return filteredPostProcessors;
}

我们看destroy() 方法:

所属类:org.springframework.beans.factory.support.DisposableBeanAdapter

public void destroy() {
   if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
      for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
         // 调用到了InitDestroyAnnotationBeanPostProcessor中的该方法,这个类的子类就是CommonAnnotationBeanPostProcessor
         processor.postProcessBeforeDestruction(this.bean, this.beanName);
      }
   }

   if (this.invokeDisposableBean) {
      if (logger.isTraceEnabled()) {
         logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
      }
      try {
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
               ((DisposableBean) this.bean).destroy();
               return null;
            }, this.acc);
         }
         else {
            // 判断该类是否实现DisposableBean接口,调用destroy()
            ((DisposableBean) this.bean).destroy();
         }
      }
      catch (Throwable ex) {
         String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
         if (logger.isDebugEnabled()) {
            logger.warn(msg, ex);
         }
         else {
            logger.warn(msg + ": " + ex);
         }
      }
   }

   if (this.destroyMethod != null) {
      // 调用自定义的destroy-method方法
      invokeCustomDestroyMethod(this.destroyMethod);
   }
   else if (this.destroyMethodName != null) {
      Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
      if (methodToInvoke != null) {
         invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
      }
   }
}

自定义类实现DisposableBean接口,重写destroy() 方法:

@Component
public class DestoryBean implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("==DestoryBean.destroy");
    }
}

另外,带注解@PreDestroy的方法:

@Component
public class PostConstructBean {

    @PreDestroy
    public void desy() {
        System.out.println("----desy");
    }
}

测试:

@Test
public void testDestroy() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    applicationContext.getBeanFactory().destroyBean("jack");
    applicationContext.getBeanFactory().destroySingletons();
}

        Bean 是在什么时候被销毁呢,在 tomcat 关闭的时候就会调用到 servlet 中的销毁方法,具体是通过 ContextLoaderListener类中的 contextDestroyed 方法,通过 closeWebApplicationContext 方法一直往下找,此为 servlet 规范的使用,在这个方法中就会最终掉用到 DisposableBeanAdapter 类的 destroy()方法。

        在Spring容器关闭过程时:

        1. 首先发布ContextClosedEvent事件;
        2. 调用lifecycleProcessor的onCloese()方法;
        3. 销毁单例Bean:
                (1) 遍历disposableBeans;
                        a. 把每个disposableBean从单例池中移除;
                        b. 调用disposableBean的destroy()方法;
                        c. 如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean;
                        d. 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉。
                (2)清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的
beanName;
                (3)清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的
beanName数组;
                (4)清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean。

        Spring的Bean在为我们服务完之后,马上就要消亡了(通常是在容器关闭的时候),别忘了我们的自定义销毁逻辑,这时候Spring将以回调的方式调用我们自定义的销毁逻辑,然后Bean就这样走完了光荣的一生!

        我们再通过一张图来一起看一看Bean实例化阶段的执行顺序是如何的?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码之Bean的生命周期 的相关文章

  • 使用祖父母接口的默认方法

    我完全不明白为什么这行不通 interface Test default void doMagic System out println Abracadabra class TestImpl implements Test class Sp
  • 访问 java jigsaw 模块中的资源文件[重复]

    这个问题在这里已经有答案了 我正在尝试从项目中的类访问 Eclipse 项目中的文件 我需要将该项目声明为 jigsaw 模块才能从其他项目访问它 但是通过这样做 我无法再访问项目中的 example png 等文件 这是我的项目结构 pr
  • 使用 python 中的 java 库

    我有一个 python 应用程序和 java 应用程序 python 应用程序为 java 应用程序生成输入并在命令行上调用它 我确信一定有一个更优雅的解决方案 就像使用 JNI 从 Java 调用 C 代码一样 有什么指点吗 仅供参考 我
  • 当派生类中重写该方法时,如何使用派生类 Object 调用基类方法?

    class A public void m1 System out println hi base class class B extends A public void m1 System out println hi derived p
  • Spring MVC - 为什么部署上下文时出现 NoSuchMethodError 异常?

    尽管这个项目已经为我工作了一段时间 但现在当我尝试在 Tomcat 中部署应用程序上下文时遇到异常 Servlet testapp threw load exception java lang NoSuchMethodError org s
  • Java 中的逻辑回归

    我们需要用 Java 进行逻辑回归 我们在 Python 中使用了这段代码http blog smellthedata com 2009 06 python logistic regression with l2 html http blo
  • 获取运行时提供的类名的 n 维数组的类

    给定一个完全限定的类名和多个维度 我想获取该类的类名 我相信我可以这样做 public Class elementType Class forName className return Array newInstance elementTy
  • 如何使用spring data mongodb在mongodb中创建视图

    如何使用spring data mongodb在mongodb中创建视图 谢谢 您可以在需要的地方注入 org springframework data mongodb core MongoTemplate 类型的 bean 并使用其方法之
  • Spring Boot,使用 EhCache 进行缓存

    我需要在我的应用程序中缓存一些数据 我正在考虑使用 Ehcache 我有几个问题 Ehcache需要另外一台服务器吗 我需要其他客户端来使用 Ehcache 吗 Ehcache 如何与多个实例配合使用 是否有可能使用 Ehcache 创建类
  • 如何为我的数独游戏制作 GUI? (摇摆)

    到目前为止 我已经编写了生成随机 9x9 数独网格的代码 我是Java的初学者 所以我有一些关于如何做UI的问题 显示数字的最佳方式是什么 我尝试创建 81 个 JTextFields 这非常乏味 而且我确信有一种有效的方法可以做到这一点
  • 与 Java 中的同步块相比,新的 Lock 接口有什么优势?

    与 Java 中的同步块相比 新的 Lock 接口有什么优势 您需要实现一个高性能缓存 允许多个读取器但单个写入器保持完整性 您将如何实现它 锁的优点是 让他们公平是可能的 可以使线程在等待 Lock 对象时响应中断 可以尝试获取锁 但如果
  • 如何在 groovy 中将输出重定向到 stderr?

    我正在寻找一种将 groovy 脚本中的输出重定向到 stderr 的方法 catch Exception e println Want this to go to stderr 就在我的脑海中 你不能做一些自我接线吗 def printE
  • Cordova Android 应用程序中的网页不可用

    编辑 我一直在解决这个问题并回顾我的所有步骤 我很乐意缩小这个问题的规模 并在令人困惑的情况下获得更多确切的细节 目前 我觉得 Keycloak 似乎只想将我重定向到 https 据我所知 这应该是 Wildfly 服务器配置问题 编辑 我
  • 定时器启动/停止参数

    自从加入这个社区以来 我在技能和进步方面取得了突飞猛进的进步 你们都是一个巨大的帮助 我无法提供一个计时器 该计时器已在启动和停止时实现了某些参数 我要么收到错误消息 局部变量计时器可能尚未初始化 要么没有收到错误消息 但什么也没有发生 也
  • java:验证 GUI 中的所有文本字段是否已完成

    我正在尝试创建一个允许某人设置帐户的 GUI 我想验证按下创建帐户按钮时所有文本字段是否完整 做这个的最好方式是什么 我正在附加我的代码 但我对文本字段是否完整的验证不起作用 参见下面的代码 public class GUIaccounts
  • jstack 是否停止在较新的 JDK8 版本上工作?

    我惊讶地发现 不知何故 最近 jstack 停止了在较新的 JDK 8 上的工作 我不确定这发生在哪个版本 但我确实得到 36649 Unable to open socket file target process not respond
  • Java中如何对整数除法进行四舍五入并得到int结果? [复制]

    这个问题在这里已经有答案了 我刚刚写了一个小方法来计算手机短信的页数 我没有选择使用Math ceil 老实说 它看起来很丑陋 这是我的代码 public class Main param args the command line arg
  • 客户端如何获取session id? (网络套接字)

    有什么办法可以做到这一点吗 客户端 function connectWebSocket var socket new SockJS socket stompClient Stomp over socket stompClient conne
  • ByteBuddy 变基、合成类型和 OSGi

    我为 byte buddy 开发了以下拦截器 public class SecurityInterceptor RuntimeType public static Object intercept SuperCall Callable su
  • hibernate通过主键查询

    我想通过主键创建查询 假设我有类主键 PersonKey 属性是 name 和 id 我有 Person 类 属性是 PersonKey 地址 DOB 现在 我想通过主键搜索人员 首先 我创建 PersonKey 的实例 并将名称设置为 j

随机推荐

  • 在Linux系统上用C++将主机名称转换为IPv4、IPv6地址

    在Linux系统上用C 将主机名称转换为IPv4 IPv6地址 功能 指定一个std string类型的主机名称 函数解析主机名称为IP地址 含IPv4和IPv6 解析结果以std vector
  • vue div高度自适应

    1 在 js文件中编写自定义指令 export default install Vue 在组件标签上绑定 v resizable 指令 并使用对象的形式通过绑定值传递宽度和高度以及最大 最小高度的值 在 bind 函数中 获取传递的值 并根
  • 走进区块链企业 I 用实践赋能实体产业,坚持提供价值服务的旺链科技

    作为华东师范大学MBA高材生 他在高科技制造 金融行业有着超过16年的业务咨询管理和技术架构经验 他是中国云体系产业创新联盟理事会常务理事 边缘计算产业联盟专家委员 也是原 Accenture资深总监 集成技术专家 而在如今话题正盛的 区块
  • linux创建,恢复和删除screen

    学习记录 侵删 目录 1 创建 2 恢复 3 删除 使用服务器训练模型时 如果服务器断开 之前的训练结果显示的终端就不好找到了 貌似可以通过线程去恢复 没试过 可以使用screen 训练前先打开一个screen 如果服务器断开 重连后可以恢
  • 最新免费版 Office 全家桶Copilot,Gamma+MindShow 两大ChatGPT AI创意工具GPT-4神器助力高效智能制作 PPT,一键生成,与AI智能对话修改PPT(免安装)

    目录 前言 ChatGPT MindShow 1 使用ChatGPT工具生成PPT内容 2 使用MindShow工具一键智能制作PPT MindShow简介 使用网页版制作 pdf转ppt GAMMA AI神器 GAMMA app介绍 注册
  • MySQL基础篇:sql_mode配置

    文章目录 零 简介 一 sql mode常用来解决的几类问题 二 sql mode包含的模式 三 sql mode各个选项作用示例 3 1 sql mode为空 对于不符合定义的值 会截断到符合定义类型 3 2 sql mode为ANSI
  • 编程语言用 Java 开发一个打飞机小游戏(附完整源码)

    编程语言用 Java 开发一个打飞机小游戏 附完整源码 上图 写在前面 技术源于分享 所以今天抽空把自己之前用java做过的小游戏整理贴出来给大家参考学习 java确实不适合写桌面应用 这里只是通过这个游戏让大家理解oop面向对象编程的过程
  • 【React】路由(详解)

    目录 单页应用程序 SPA 路由 前端路由 后端路由 路由的基本使用 使用步骤 常用组件说明 BrowserRouter和HashRouter的区别 路由的执行过程 默认路由 精确匹配 Switch的使用 重定向路由 嵌套路由 向路由组件传
  • 计算机网络体系结构 - 运输层

    一 运输层协议概述 运输层为应用进程之间提供端到端的逻辑通信 二 运输层的端口 端口 port 也称为协议端口号 protocol port number 对上层的应用进程进行标识 端口用一个16位端口号进行标志 端口号只具有本地意义 端口
  • 剑指offer-输出字符串所有种类的排列组合

    常规题 先校验长度 不符合则直接输出 符合则判断是否为最后一个字符 是则直接new对象输出 不是则交换begin和i位置的数字 再用递归输出 public class Test28 先校验 public static void permut
  • 笔试

    文章目录 前言 40 复位电路设计 1 recovery time和removal time 2 同步复位和异步复位 3 异步复位同步释放 本文参考 往期精彩 前言 嗨 今天来学习复位电路设计相关问题 微信关注 FPGA学习者 获取更多精彩
  • cec2017(python):红狐优化算法(Red fox optimization,RFO)求解cec2017

    一 红狐优化算法 红狐优化算法 Red fox optimization RFO 由Dawid Po ap和 Marcin Wo niak于2021年提出 该算法模拟了红狐的狩猎行为 具有收敛速度快 寻优精度高等优势 参考文献 Poap D
  • easyexcel读取excel将数据存到mysql【一个简单的例子】

    读取excel 1 xml里面增加maven
  • 使用Java程序向手机发送短信

    JAVA发送手机短信 有几种方法 1 使用webservice接口发送手机短信 这个可以使用sina提供的webservice进行发送 需要进行注册 2 使用短信mao的方式进行短信的发送 这种方式应该是比较的常用 前提是需要购买硬件设备
  • 变分推断

    一 概述 对于概率模型来说 如果从频率派角度来看就会是一个优化问题 从贝叶斯角度来看就会是一个积分问题 从贝叶斯角度来看 如果已有数据 x x x 对于新的样本 x hat x
  • 转债打新监听

    不炒股 只打新捡点小钱 package com github niefy modules job run import cn hutool core convert Convert import cn hutool core util Nu
  • 软件测试基础知识个人笔记

    一 为什么要测试 软件系统越来越成为生活中不可或缺的一部分 缺陷不可避免 软件不正确执行可能会导致很多问题 二 软件测试的定义 软件测试 描述一种用来促进鉴定软件的正确性 完整性 安全性和质量的过程 维基百科 公司主流观点 测试是一个包含计
  • eclipse注释模板

    eclipse注释模板 一 配置步骤 Eclipse中 gt Window gt Preferences gt Java gt Code Style gt Code Templates 二 模板示例 Files 文件 Title file
  • java 多线程执行时间测试,TestNG中实现多线程并行,提速用例的执行时间

    TestNG是一个开源自动化测试工具 TestNG源于Junit 最初用来做单元测试 可支持异常测试 忽略测试 超时测试 参数化测试和依赖测试 除了单元测试 TestNG的强大功能让他在接口和UI自动化中也占有一席之地 以Java为例 目前
  • Spring源码之Bean的生命周期

    Spring已经成为了目前最流行的第三方开源框架之一 我们在充分享受Spring IOC容器带来的便捷时 也应该考虑一下Spring这个大工厂是如何将一个个的Bean生产出来的 我们一起来讨论一下Spring中Bean的生命周期 Sprin