4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

这篇具有很好参考价值的文章主要介绍了4.是人就能学会的Spring源码教程-IOC容器创建Bean对象。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

IOC容器创建Bean对象

简单了解Bean工厂

我们要关注一个接口BeanFactory,它是Spring IOC容器的根接口,也是容器的入口。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

类的描述中已经清楚的说明了:

用于访问 Spring bean 容器的根接口。
这是 bean 容器的基本客户端视图;进一步的接口,如ListableBeanFactory和org.springframework.beans.factory.config.ConfigurableBeanFactory可用于特定目的。

我们来看一下这个接口里面的方法。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

我们可以看到有各种各样的getBean方法,让我们可以从容器中获取到各种各样的Bean对象。

BeanFactory有一个实现类DefaultListableBeanFactory我们要重点关注下。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

这个类的类图如下图所示:

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

创建Bean对象

有了Bean的定义信息之后,Spring容器就可以开始创建对象了。

Bean对象的创建分为实例化初始化两个步骤。

在我们日常使用Java创建对象的过程中,可能对这两个步骤没有分得这么清楚。

实例化就是给Bean对象开辟空间进行存储,并给对象里的属性赋初始值的过程。

初始化就是给对象做一些初始化的操作,例如填充属性、执行初始化方法。接下来我们就来聊聊Bean对象的实例化和初始化的过程。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

实例化

在之前的介绍中,我们也已经知道了,Spring Bean对象的创建是通过反射的方式实现的。但是口说无凭,我们来看看代码。

@SpringBootApplication
public class SpringCodeStudyApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringCodeStudyApplication.class, args);
	}
}

我们从Spring boot程序启动的地方开始看起,上面的代码我们一定不陌生,因为这个是每一个Spring Boot程序启动的地方。我们执行这个main方法,即可启动Spring Boot应用程序。

它的底层是怎么做到的呢?

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

我们进一步点击SpringApplicationrun方法,发现run方法又调用了另外一个重载的run方法,最后调用的是SpringApplication这个类中的非静态的run方法。

我们点击进入这个非静态run方法看看。

	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		...
	}

这个run方法的注释告诉我们,这个方法运行Spring应用程序,创建并刷新一个新的ApplicationContext

这个ApplicationContext是什么?

我们去查看类图,发现了它是BeanFactory的子接口,换句话说,它就是Spring容器啊。这意味着在这个方法里将会创建并且准备好我们要使用的IOC容器。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

那我们进一步看看这个方法里做了什么?

...
context = createApplicationContext();
...
refreshContext(context);

createApplicationContext方法,从意思上我们也可以理解,这个方法创建了IOC容器。关键是refreshContext方法是做什么用的呢?

我们进一步查看这个方法。

	private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			shutdownHook.registerApplicationContext(context);
		}
		refresh(context);
	}
	
	/**
	 * Refresh the underlying {@link ApplicationContext}.
	 * @param applicationContext the application context to refresh
	 */
	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}

我们发现refreshContext方法最后调用了容器applicationContextrefresh方法。

我们查看这个方法的注释,我们发现了,原来这个方法内部实例化了所有的单例Bean对象。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

所以我们得出了结论,refreshContext方法是对容器进行初始化操作的,至少包含了对容器内单例Bean对象的创建。

这不就是我们要找的方法吗?看来我们要找的Bean对象实例化的代码就在这个applicationContextrefresh方法里面。

我们进入refresh方法看看。

我们发现这个方法有三个实现,不过看起来,AbstractApplicationContext这个类比较像是我们要找的目标。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

进入到AbstractApplicationContext类的refresh方法,我们发现了这样的代码。

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

注释中表示finishBeanFactoryInitialization方法会实例化单例Bean对象,说明了这个是我们的目标。

我们进入finishBeanFactoryInitialization方法看看,发现了实例化单例对象的其实调用的是Bean工厂beanFactorypreInstantiateSingletons方法。

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

这里的beanFactory其实指的是DefaultListableBeanFactory这个类。

进入到DefaultListableBeanFactorypreInstantiateSingletons方法,我们发现了我们距离Bean对象实例化的代码越来越近了。

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				...
				else {
					getBean(beanName);
				}
			}
		}

这段代码告诉我们,它将会触发所有单例Bean对象的初始化。它这里做了判断,根据Bean的定义信息,也就是BeanDefinition,这个Bean必须是非抽象的,单例的,且不是懒加载的,才会被创建。

而创建的方法就是调用getBean方法,传入了Bean的名称。

我们进入getBean方法。

	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

发现getBean方法调用了doGetBean方法。看到这个以do开头的方法,我们就要注意了,真正关键的代码就快要出现了。

进入doGetBean方法,我们发现了代码。

createBean(beanName, mbd, args);

进一步进入createBean方法,发现它的实现类在AbstractAutowireCapableBeanFactory上,它是AbstractBeanFactory的子类。

4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

createBean方法中,我们发现了如下代码

		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}

又是一个do开头的方法,我们知道,我们又得打起精神了,这里就是创建Bean对象的实际操作方法。

进一步进入doCreateBean方法。

		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

我们发现了createBeanInstance的方法,心态要崩了呀,Spring的源码怎么嵌套地这么深,要看一个实现咋就这么难。让我们打起精神,胜利就在前方,就快要找到创建Bean对象的方法了。

我们进入createBeanInstance方法,看到了如下代码。

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);

这段代码表示使用无参的构造器,我觉得我们就快要找到反射创建Bean对象的代码了。

进入instantiateBean方法,我们看到了代码。

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);

我的天啊,胜利的曙光就要来了。这个代码显示获取实例化策略来实例化代码。

我们进入instantiate方法,我们发现进入的类是SimpleInstantiationStrategy

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

在这代码里,我们发现了什么?

Constructor<?>类型,终于出现了反射部分的代码。这个方法先获取到了Bean对象实例化要用到的构造器对象,再调用BeanUtils.instantiateClass方法进行实例化。

我们继续进入BeanUtils.instantiateClass方法。

	public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		...
				return ctor.newInstance(argsWithDefaultValues);
		...
	}

终于,我们要找到的反射代码终于出现了。

Spring通过获取到Bean对象的构造器,并调用了newInstance方法实例化了对象。文章来源地址https://www.toymoban.com/news/detail-452981.html

到了这里,关于4.是人就能学会的Spring源码教程-IOC容器创建Bean对象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring IoC容器、IoC与DI

    目录 Spring是什么? 理解容器  什么是IoC(Inversion of Control) 传统的new创建对象的方式中类与类的耦合程度很大。  IoC的优势:  Spring IoC容器最核心的功能  什么是DI (Dependency Injection) IoC和DI的区别  Spring是指Spring Framework(Spring框架),它是开源的框架,有着很庞大的社区,通过

    2023年04月21日
    浏览(76)
  • spring ioc容器

    ioc是 inversion of Control的简写,意为控制反转。通过其对所有的Java对象的实例化和初始化,控制对象与对象之间的依赖关系。 (1)控制反转是一种思想。 (2)控制反转是为了 降低程序耦合度,提高程序扩展力。 (3)控制反转,反转的是什么? 答:将对象的创建权利交出去

    2024年01月17日
    浏览(29)
  • Spring核心容器IOC案例讲解,带你理解IOC

    Universe Infinity inc. 什么是IOC容器,先把IOC给忽略到,其实就是个容器。 什么?容器又是个啥玩意?容器是用来放东西的东西啊。 各个领域都喜欢起一些专业术语,显得很高级。给你讲IOC是不是很高级,给你讲Map是不是就明白了。 bean对象最终存储在spring容器中,在spring源码底

    2024年01月24日
    浏览(28)
  • Spring——Spring是什么?IoC容器是什么?

    本人是一个普通程序猿!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果你也对编程感兴趣的话,互关一下,以后互相学习,共同进步。这篇文章能够帮助到你的话,劳请大家点赞转发支持一下! 我们通常所说的 Spring 指的是 Spring Framework(Spring 框架) ,它是

    2024年02月15日
    浏览(26)
  • 3、Spring 之IOC 容器 详解

    IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种 设计思想 ,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器 来管理 所有 Java 对象的实例化和初始化 , 控制对象与对象之间的依赖关系 。我

    2024年02月09日
    浏览(26)
  • Spring 6.X IoC 容器

    下面主要介绍 Spring 框架对控制反转 (IoC) 原理的实现 首先要说明的是:IoC 也称为依赖注入,这是一个过程。 其次依赖项的定义:对象仅通过构造函数参数、工厂方法的参数,或在构造对象实例、工厂方法返回后在对象实例上设置的属性来定义其依赖项(即它们使用的其他对

    2024年02月09日
    浏览(26)
  • Spring 核心之 IOC 容器学习二

    Annotation 的前世今生 定位 Bean 扫描路径 读取 Annotation 元数据 扫描指定包并解析为 BeanDefinition

    2024年01月19日
    浏览(37)
  • Spring 核心之 IOC 容器学习一

    1、BeanFactory 其中 BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,BeanFactory 有三个重要的子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。 那

    2024年01月19日
    浏览(33)
  • spring6-IOC容器

    IOC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将

    2024年02月08日
    浏览(29)
  • spring6-实现简易版IOC容器

    我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。 1、回顾Java反射 Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调

    2024年02月08日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包