通过springboot autoconfig分析bean实例化过程的案例

2023-11-10

分析案例前提所参考的例子,Spring Boot中的自定义start pomSpringBoot学习笔记(3) Spring Boot 运行原理,自动配置

,然后我又看了下nacos的源码,发现原来也都是这做的截图如下:

当然最终的目的我是想知道@Bean  helloService实例化过程


/**
 * @desc 自定义自动配置加载类:
 * 1、编写属性配置类,读取application.properties文件初始化属性
 * 2、编写service类。
 * 3、编写自定义自动加载配置类
 * 4、在配置类中根据HelloServiceProperties提供的参数,并通过ConditionalOnMissingBean判断
 * 容器中没有bean时,注入helloService。
 */
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enabled", matchIfMissing = true)
public class HelloServiceAutoConfiguration {

    @Autowired
    private HelloServiceProperties helloServiceProperties;

    //容器中没有指定的HelloService实例时进行初始化
    @Bean
    @ConditionalOnMissingBean(HelloService.class)
    public HelloService helloService() {
        HelloService helloService = new HelloService();
        helloService.setMsg(this.helloServiceProperties.getMsg());
        return helloService;
    }
}

1:首先回顾一下bean创建以及后期处理过程中的主要类的名字和作用,我觉得有两个类需要提前关注下,如下图所示(ctrl+h),

DefaultListableBeanFactory作为默认的bean注册加载的工厂类,继承了AutowireCapableBeanFacotry(提供创建、自动注入、初始化、和后置处理器的作用)

2:调用关系时序图参考 IDEA自动生成方法时序图 -- Sequence Diagram(一款超好用的Idea插件)

当然,时序图有时候也不是万能的。

      第一步:

此时启动springboot之后,相继进入AbstractApplicationContext类的refresh()方法,这里我只关注一个finishBeanFactoryInitialization方法,其他的并非本次分析的重点:

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
该refresh()所调用的方法注释翻译:实例化所有剩余的(非延迟初始化)单例,当然到了这一步bean已经全部加载完毕,之后的过程是进行实例化的过程

   第二步:

点击进入finishBeanFactoryINitialization方法,此时使用beanFactory就是DefaultListableBeanFactory,准备bean的实例化

/**
	 * Finish the initialization of this context's bean factory,
	 * initializing all remaining singleton beans.
	 */
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// 省略。。。。。

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}

     第三步

  分析DefaultListableBeanFactory的preInstantiateSingletons方法,

Ensure that all non-lazy-init singletons are instantiated(确保所有非懒加载的bean都实例化)
@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// 1:这里的beanDefinitionNames的存储方式这ArrayList
 	    //private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
        // 在扩展看下就是	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
        //volatile 保证了可见性和顺序性,便于判断逻辑的准确实时


		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // 这里我的类并没有实现FactoryBean的接口所以进入else的getBean方法,此处的判断逻辑是这样的:beanType != null && FactoryBean.class.isAssignableFrom(beanType)
                // 新的知识点出来了  instanceof, isinstance,isAssignableFrom的区别
                // 自身实例或子类实例 instanceof 自身类 
                // 自身类.class.isInstance(自身实例或子类实例)  返回true 
                // 自身类.class.isAssignableFrom(自身类或子类.class)  返回true 

				if (isFactoryBean(beanName)) {
					// 省略。。。
				}
				else {
					getBean(beanName);
				}
			}
		}

		// 省略。。。。
	}

    第四步:

     查看getBean方法,当然要记住,目前操作都是在DefaultListableBeanFactory,可见,这里多么重要,是bean的存储的地方即beanDefinitionMap,还有创建bean的方法。

    之后的过程会进入DefaultListableBeanFactory的父类AbstractBeanFactory执行doGetBean,这里的方法名挺有意思的,我是这么理解的 getBean -表示想要获取bean,doGetBean-那就去获取bean,之后会看到createBean-表示想要创建bean,doCreateBean-真正的创建bean,当然前提条件是DefaultListableBeanFactory里边的三级缓存无数据,这点参考《spring源码分析》对三级缓存的理解,目的是提前暴露bean避免循环依赖,从代码方面可以看出以下缓存的数据结构

/** Cache of singleton objects: bean name to bean instance. */一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */ 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

代码角度分析doGetBean的过程:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// 缓存检测实例是否提前被暴露,接下来看看里边的缓存是如何处理的
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			//省略。。。
		}

		else {
			//原型模式的bean检测是否存在循环依赖,有的话直接报异常,因为会造成死循环,最终导致程序崩溃
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// 省略。。。

			try {
				// 省略。。。

				// Create bean instance.
				if (mbd.isSingleton()) {
                    // 此处对于单例bean的创建,参数是beanNane和接口实现方法
                    // 最终调用createBean,当然还没完之后还有个doCreate真正去创建bean的方法
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// 省略。。。
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// 省略。。。
		return (T) bean;
	}

分析下第一个获取bean的方法--getSingleton,挺有意思的,对照前面提到的三级缓存结构,大致逻辑是查找一级缓存是否存在实例对象,不存在找二级缓存是否存在实例对象,不存在就再次查找三级缓存是否存在ObjectFactory,如果没有返回空,否则通过beanFactory.getBean获取实例对象,存入二级缓存,同时移除三级缓存对象。同理之后的处理逻辑会从二级缓存提取放入以及缓存,移除二级缓存对象,但是我没搞懂为啥有个二级缓存,可能为了更好的解耦合,见代码:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

第五步:

现在分析下第二个getSingleton方法,当然我们看到了第二个入参,是个匿名类,一定会调用匿名类的createBean方法,代码如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				// 省略。。。
                // 前置BeanCurrentlyInCreationException判断
                beforeSingletonCreation(beanName);
				try {
                    // 匿名类参数的调用,实际上是调用createBean方法,之后会分析createBean方法
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				// 省略。。。
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
                    // 后置BeanCurrentlyInCreationException判断
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
                    // 这块提前看下,就是把bean实例放入一级缓存,之后的调用
                    // 直接从一级缓存获取
                    // synchronized (this.singletonObjects) {
			            // this.singletonObjects.put(beanName, singletonObject);
			            // this.singletonFactories.remove(beanName);
			            // this.earlySingletonObjects.remove(beanName);
		            	// this.registeredSingletons.add(beanName);
		             // }
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

第六步:crreateBean方法,主要做了三个主要的灵魂级别的事情1:创建一个bean实例 ,2:填充bean实例 ,3:执行后置处理器

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            // 这里是创建beanInstance的过程,继续跟代码
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            // 这里是丰富化实例,执行属性注入,这里会获取beanPostProcess(getBeanPostProcessors)
            // 循环调用得到AutowiredAnnotationBeanPostProcessor,去处理@Autowired注入
            // 20200630补注:AutowiredAnnotationBeanPostProcessor类的postProcessProperties方法进行注入
            // 当然这里又会持续doGetBean的过程
			populateBean(beanName, mbd, instanceWrapper);
            // 这里执行后置处理器的过程
			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);
			}
		}

		// 省略。。。

		return exposedObject;
	}

 

当然代码继续调用的是doCreateBean方法实现这些过程的。需要额外说明的是spring保证每个bean都要执行beanPostProcess方法,内置了大概是个process方法,也可以自定义,自定义之后,启动refresh的时候,会执行registerBeanPostProcessors方法,大概过程如下:前提已经加载了左右的bean class到beanDefinitionMap容器,之后执行doGetBeanNamesForType方法

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
   List<String> result = new ArrayList<>();

   // Check all bean definitions.
   for (String beanName : this.beanDefinitionNames) {省略。。。。}

获取所有实现BeanPostProcessor接口的实现类,提取存放到beanPostProcessors里

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

最后在populateBean、exposedObject 时候会执行processor方法,给予实例化的更改实际的机会,大致代码过程

for (BeanPostProcessor processor : getBeanPostProcessors()) {
   Object current = processor.postProcessBeforeInitialization(result, beanName);
   if (current == null) {
      return result;
   }
   result = current;
}

当然当执行到ConfigurationBeanBindingPostProcessor的postProcessBeforeInitialization时候会进行@ConfigurationProperties的值反射绑定(bind-->bindDataObject),其绑定的定义值也都是从PropertySource类中获取,说的有点远了,看下PropertySource的结构

public abstract class PropertySource<T> {

	protected final Log logger = LogFactory.getLog(getClass());

	protected final String name;

	protected final T source;


	/**
	 * Create a new {@code PropertySource} with the given name and source object.
	 */
	public PropertySource(String name, T source) {
		Assert.hasText(name, "Property source name must contain at least one character");
		Assert.notNull(source, "Property source must not be null");
		this.name = name;
		this.source = source;
	}

          其实这个结构存储额所有springboot配置文件的key vale值,可以通过Environment  注入的形式获取,源头都是PropertySource,找到源头是一件非常好玩的一件事情。

 

 第七步:分析createBeanInstance方法,这里挺有意思的,普通方法例如使用@Component,直接进行构造函数实例化,但是本例中使用的是@Configuration @Bean进行实例化的,如下图,所以我猜测spring会给mdb set一个特殊实例化工厂方法,然后执行具体的策略,有兴趣的自己可以在setFacory断点查看,

如我所愿,果真如此,可以看下图,这里边的信息表明@Configuration 真的是将工厂方法策略的元数据信息set到了mdb里边,方便之后的执行操作,具体操作大家应该都能猜到,就是使用的@Bean里边自定义的实例化方法

特殊工厂方法策略实例化代码

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// 省略。。。
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// 省略。。。
		// 无特殊处理:只需使用无参数构造函数。
		return instantiateBean(beanName, mbd);
	}

第八步:

	protected BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

		return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
	}

继续跟代码,可以看到最终的猜想是否正确,到底使用工厂方法做了什么特殊处理呢?

public BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
        // 省略。。。。
        // 获取工厂
			factoryClass = factoryBean.getClass();
        // uniqueCandidate是具体@Bean注解的方法Method,之后利用反射执行实例化
            bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
			return bw;

利用工厂方法(helloService)进行反射实例化Bean的过程如下

@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, final Method factoryMethod, Object... args) {

		try {
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(factoryMethod);
					return null;
				});
			}
			else {
				ReflectionUtils.makeAccessible(factoryMethod);
			}

			Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
			try {
				currentlyInvokedFactoryMethod.set(factoryMethod);
				Object result = factoryMethod.invoke(factoryBean, args);
				if (result == null) {
					result = new NullBean();
				}
				return result;
			}
			// 省略。。。。
		}
		// 省略。。。。
	}

我的公众号:第一次想认真写写博客,写写生活点滴

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

通过springboot autoconfig分析bean实例化过程的案例 的相关文章

随机推荐

  • CentOS7安装详细教程

    VM安装CentOS 7详细教程 通过VM安装CentOS7虚拟机的全部过程 并自动配置IP地址和DNS服务器 可以进行联网 1 软件准备 VM12 软件 安装包下载地址 云盘链接 VM12软件安装包下载地址 提取码 5lgm CentOS
  • 微信企业号,回调模式开通.net

    企业号每个应用有普通模式和回调模式两种 普通模式直接打开网页 回调模式可设置应用底部菜单项 可增加交互开发 可把客户端的操作事件传给企业服务器 企业服务器做响应开发 开通回调模式 首先需要通过url的回调验证 那么进入应用后台设置项 设置好
  • Vue2学习计划二:mustache与methods和computed等Vue实例参数

    上一节写了Vue实例的生命周期 我们心里有了个Vue里的数据绑定至DOM 那么具体怎么实现的呢 要实现只需要在Vue绑定的DOM元素中使用mustache语法即可 简单例子如下 div h2 message h2 hr h3 全名 full
  • (一)ideal 创建springboot工程和实现简单配置

    新建 IDEA project 选择Spring Initializr Choose Initializr Service URL 选择 Default Https start spring io 点击 next 进入下一步 提示 1 sp
  • Keil for arm 关于enit0 快速中断(FIQ)的响应

    本文原创 版权所有 如需转载 请注明出处 接着上篇讲arm7对于普通中断的响应 今天讲一下 关于快速中断的响应 步骤1 基础环境 arm7 LPC2106 Realview 4 2 编译环境默认 步骤2 starup s文件 启动代码 和i
  • adb shell 报错error: device unauthorized

    2022 7 29 oppo r11s 安卓8 亲测成功 windows电脑在链接安卓设备后 想要进行终端命令行进入到该设备 出现报错 报错内容 C Users gt adb shell error device unauthorized
  • 机械手使用者坐标系和工具坐标系_发那科机器人应用-坐标系介绍(2)

    工具坐标系 由工具中心点 的位置 和工具的姿势 构成 工具中心点 的位置 通过相对机械接口坐标系的工具中心点的坐标值 来定义 工具的姿势 通过机械接口坐标系的 轴 轴 轴周围的回转角 来定义 工具中心点用来对位置数据的位置进行示教 在进行工
  • Linux系列:Linux中如何安装.rpm、.tar、.tar.gz和tar.bz2文件

    我以下面三个包为例 三个包都在 etc opt下 A example 1 2 3 1 rpm B example 1 2 3 1 tar C example 1 2 3 1 tar gz 1 安装rpm包 说起RPM REDHAT Pack
  • 关于QtCreator 4.8 创建工程时,选中创建界面(.ui)无法创建工程问题

    在主界面一次选中 help gt about plugins 然后在弹出界面中找到QtCreator 在Designer右侧的的方框中打钩 然后close界面 重启软件 搞定 很简单的设置 浪费了好长时间
  • 芯片设计制造全过程

    芯片设计制造全过程 将一颗芯片从0到1 可以分为芯片设计和芯片制造两部分 芯片设计对应市场上一些fabless公司 这类公司只做芯片设计 而芯片制造对应的是foundary 比如国内的smic TSMC 国外的Samsung GlobalF
  • 分布式系统的接口幂等性设计

    什么是幂等性 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的 不会因为多次点击而产生了其他的结果 在 CRUD 这 4 个操作中 查询操作是天然幂等的 删除操作也只会删除一次 多次的删除操作结果还是一样 影响幂等的只有在添加和
  • 服务器考试系统推荐,如何挑选并发性好的在线考试系统?

    原标题 如何挑选并发性好的在线考试系统 并发性是在线考试系统的核心指标之一 不同的在线考试系统之间可能功能上看似差异不大 但并发性不同就会使得使用体验悬若霄壤 如何挑选并发性好的在线考试系统呢 最好的办法还是通过具体的使用来进行判断 什么是
  • ConstraintLayout 使用详解

    在安卓开发过程中除了一些列的逻辑代码程序以外 UI界面也是很重要的组成部分 对于用户来说界面更是一个应用的所有 所有的操作和交互都是在UI发生 对于开发者来说UI的加载和绘制对应用的计算和内存的影响更是不容忽视 下面看一组界面 这个布局很简
  • 线程池使用和自定义线程池

    目录 1 线程基础概述 1 1 线程池的作用 1 2 为什么要用线程池 1 3 比较重要的几个类 1 4 new Thread的弊端 2 四种线程池 2 1 源码分析 2 2 RejectedExecutionHandler 线程池四种拒绝
  • subversion强制写log的windows 和linux hooks脚本

    windows code echo offsetlocalset REPOS 1set TXN 2rem check that logmessage contains at least 10 characterssvnlook log RE
  • Tinyid原理介绍

    Id生成系统要点 在简单系统中 我们常常使用db的id自增方式来标识和保存数据 随着系统的复杂 数据的增多 分库分表成为了常见的方案 db自增已无法满足要求 这时候全局唯一的id生成系统就派上了用场 当然这只是id生成其中的一种应用场景 那
  • MYSQL 中的表空间

    MYSQL中的表空间 MYSQL 的表空间可以理解为其存储在物理磁盘上的 表名 ibd 文件 MYSQL 为了管理方便 增加了很多概念 来一一了解一下 逻辑概念 Segment 在 B 树的根页面的中 存储了两个 10 字节的字段 PAGE
  • vue实现动态锚点

    监听滚动事件 实现动态锚点 参考 Vue监听滚动实现锚点定位 双向
  • 科技赋能企业,实现数字化转型

    科技是第一生产力 数字技术即科技 可以改变传统的商业模式 为各行各业注入新的活力 推动企业数字化转型 可是实现行业的效率提升 实现跨界重组 重构产业模式 为产业格局重新赋能 最终实现企业产能升级 一 科技赋能意义重大 科技赋能企业 为我国经
  • 通过springboot autoconfig分析bean实例化过程的案例

    分析案例前提所参考的例子 Spring Boot中的自定义start pom SpringBoot学习笔记 3 Spring Boot 运行原理 自动配置 然后我又看了下nacos的源码 发现原来也都是这做的截图如下 当然最终的目的我是想知