ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码解析二十七

2022-03-20 23:01:01  阅读:165  来源: 互联网

标签:InitializingBean 实例 mbd Spring 二十七 bean 源码 null 方法


前面我们已经了解了Spring如何为bean填充属性,而且,在属性填充时还涉及到三种自动装配模式,分别是根据名称、类型以及构造方法来自动装配bean的属性。完成bean属性装配之后,接下来就要开始bean的初始化了。我们回到之前的源码看下:
在这里插入图片描述
我们到initializeBean方法中看下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 调用各种Aware接口
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// bean 初始化前调用bean的后处理器
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 执行bean的初始化方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// bean 初始化后,调用bean的后处理器
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

在这里插入图片描述
在方法initalizeBean中,首先会执行方法invokeAwareMethods,我们到invokeAwareMethods中看下:
在这里插入图片描述
Spring 在bean的实例化时会调用各种Aware接口,将Spring容器内相应的组件注入到bean的实例中。
我们继续看InitalizeBean方法后面的逻辑:
在这里插入图片描述
我们先看方法applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization:
在这里插入图片描述
在这两个方法中就是在执行后处理器BeanPostProcessor相关的逻辑,这里执行的就是最底层的BeanPostProcessor中的前置和后置处理方法。我们在前面也看到bean的加载逻辑,也会执行很多后处理器,但是那些后处理器都是在执行它们自己拓展的一些后处理器的方法而已。而实际底层的接口BeanPostProcessor中的方法,却没有执行。在这里才会统一的执行BeanPostProcessor中的方法,为bean的实例进行一些初始化的操作。

我们在看下方法invokeInitMethods:
在这里插入图片描述
这个是执行bean的初始化方法,我们到invokeInitMethods方法中看下:

	protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		// bean 是否是InitializingBean的实例
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						// 执行InitializingBean中的方法afterPropertiesSet
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				// 执行InitializingBean中的方法afterPropertiesSet
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			// 获取初始化方法的名称
			String initMethodName = mbd.getInitMethodName();
			// bean指定了初始化方法
			if (StringUtils.hasLength(initMethodName) &&
					//并且不是InitializingBean的实例,指定的初始化方法也不是InitializingBean中的接口方法
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					// 并且初始方法也不是外部管理
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				// 执行定制化的初始化方法
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

在这里插入图片描述
可以看到在invokeInitMethods方法中,首先会判断当前bean是否是接口InitializingBean的实例,如果是的话,就会执行InitializingBean中的方法afterPropertiesSet。那这个方法afterPropertiesSet到底是干什么的呢?我们到InitializingBean中看下:
在这里插入图片描述
在InitializingBean中只有一个afterPropertiesSet方法。通过注释我们大概可以知道方法afterPropertiesSet调用时机,是在bean实例化完成所有属性设置之后,而且方法afterPropertiesSet的作用,主要是对bean实例进行一些配置和最终的初始化操作。

我们通过一个案例来体验下InitializingBean如何使用?

在这里插入图片描述
我们创建一个User7类并且实现了接口InitializeingBean。
将User7配置到xml中:
在这里插入图片描述
测试一下:
在这里插入图片描述
执行结果:
在这里插入图片描述
我们继续看下invokeInitMethods后面的逻辑:
在这里插入图片描述
接下来会通过getInitMethodName,从mbd也就是bean的BeanDefinition中获取bean的初始化方法。那这个bean的初始化方法是什么呢?我们通过案例体验一下:
在这里插入图片描述
我们在User7中添加一个init方法。
我们修改下xml文件:
在这里插入图片描述
我们在xml中新增属性init-method.。
我们执行一下程序,看下结果:
在这里插入图片描述
可以看到bean初始化时执行了我们指定的方法。
在这里插入图片描述
而这个getInitMethodName其实就是我们在标签中指定的初始化方法名称,并且接下来会在方法invokeCustomInitMethod执行我们自定义的的初始化方法。我们在到invokeCustomInitMethod方法中看下:
在这里插入图片描述
在方法invokeCustomInitMethod中,其实就是通过反射来执行我们的方法。
在看我bean的初始时,我们在回到doCreateBean方法,看下最后的一些逻辑:
在这里插入图片描述
前面我们知道Spring默认是允许曝光早期单例bean的,所以earlySingletonExposure是为true的。接下来会调用方法getSingleton,从缓存中获取单例bean。因为allowEarlyReference参数传入的是false表示不允许早期引用,也就是不允许从工厂缓存获取早期的单例bean,所以方法getSingleton调用的时候得到的值earlySingletonReference是等于null的。

接下来,Spring会注销bean相关的方法,我们接着往下看:
在这里插入图片描述
我们到方法registerDisposableBeanIfNecessary中看下:
在这里插入图片描述
在这里插入图片描述
可以看到方法registerDisposableBeanIfNecessary主要是为bean注册DisposableBeanAdapter,而DisposableBeanAdapter就是在bean销毁时进行调用的。
在这里插入图片描述
DisposableBeanAdapter默认是实现DisposableBean接口的,那接口DisposableBean有是什么呢?
在这里插入图片描述
在DisposableBean接口中只有一个方法destroy,而且通过注释我们也知道destroy会在bean销毁之前被Spring容器调用执行。
我们通过一个案例看下DisposableBean如何使用?
在这里插入图片描述
类似InitializingBean,我们的User7实现了接口DisposableBean。
我们测试一下:
在这里插入图片描述
测试结果:
在这里插入图片描述

既然接口DisposableBean和接口InitializingBean类似,那销毁bean时有没有类似bean标签中属性init-method这样的,可以指定bean中的方法为销毁方法的机制呢?答案当然也是有的,我们也来看下:
在这里插入图片描述
我们修改下xml文件:
在这里插入图片描述
我们运行程序看下结果:
在这里插入图片描述
从这个结果我们发现是先执行DisposableBean接口中的destory方法,接着执行我们自定义的销毁方法。我们可以到DisposableBeanAdapter寻找答案。作为DisposableBean接口的实现类,DisposableBeanAdapter当然也实现了destory方法,接下来,我们来看下Spring默认注册的DisposableBeanAdapter,在destory方法中具体干了哪些事情:

	@Override
	public void destroy() {
		// 如果注册了销毁bean之前处理的后处理器那就先执行处理后处理器的逻辑
		if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
				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 {
					// 如果bean 实现了接口DisposableBean当bean被销毁时就会执行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);
				}
			}
		}
		// 如果bean配置了和初始化方法对应的销毁方法
		if (this.destroyMethod != null) {
			// 执行自定义的销毁方法
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName != null) {
			Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
			if (methodToInvoke != null) {
				invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
			}
		}
	}

在这里插入图片描述
可以看到,在destory方法中,Spring首先给我们提供了一个后处理器的扩展点,也就是自定义实现后处理器DestructionAwareBeanPostProcessor中的方法postProcessBeforeDestruction,这样的话,我们在指定bean销毁前就可以做一些事情了。
我们再看下后面的逻辑:
在这里插入图片描述
如果成员变量invokeDisposableBean为true,且当前bean实例是DisposableBean的实例,就会执行bean的destory方法。那成员变量invokeDisposableBean的值是否为true呢?我们来看下:
在这里插入图片描述
在DisposableBeanAdapter的构造方法中,发现如果当前实例bean是DisposableBean接口的实例,invokeDisposableBean就为true,也就是会执行bean中的destory方法。

接下来,我们最后来看下destory方法剩下的一些逻辑:
在这里插入图片描述
很显然,这里就是执行属性destory-method指定的方法了,和前面执行init-method方法是类似的。

截止到这里,bean加载的逻辑基本上都已经分析完毕,我们回到单例bean创建的位置来看下:
在这里插入图片描述
可以看到,当单例bean实例化之后,和我们之前从缓存中获取单例bean类似,接下来还会调用方法getObjectForBeanInstance,看下bean是否是FactoryBean的实例,并且是否需要通过FactoryBean来实例化bean。
最后,就是对单例之外的其他类型bean的实例化了,比如prototype等:
在这里插入图片描述在这里插入图片描述
其他类型bean的实例化和单例bean的实例化,在主流程上都比较类似的,可以看到,Spring最后会将实例化好的bean直接返回,截止到这里,bean的加载流程算是结束了。
通过一张图来梳理下bean加载的流程:
在这里插入图片描述

标签:InitializingBean,实例,mbd,Spring,二十七,bean,源码,null,方法
来源: https://blog.csdn.net/qq_35873436/article/details/123621545

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有