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

IOC容器创建Bean对象

简单了解Bean工厂

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

image-20230516223651821

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

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

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

image-20230516223927341

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

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

image-20230516224734506

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

image-20230516224848083

创建Bean对象

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

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

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

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

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

image-20230516231709467

实例化

在之前的介绍中,我们也已经知道了,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容器。

image-20230519234749938

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

...
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对象。

image-20230520002449825

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

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

我们进入refresh方法看看。

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

image-20230520002911269

进入到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的子类。

image-20230520004646478

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方法实例化了对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值