IOC容器创建Bean对象
简单了解Bean工厂
我们要关注一个接口BeanFactory
,它是Spring IOC容器的根接口,也是容器的入口。
类的描述中已经清楚的说明了:
用于访问 Spring bean 容器的根接口。
这是 bean 容器的基本客户端视图;进一步的接口,如ListableBeanFactory和org.springframework.beans.factory.config.ConfigurableBeanFactory可用于特定目的。
我们来看一下这个接口里面的方法。
我们可以看到有各种各样的getBean
方法,让我们可以从容器中获取到各种各样的Bean对象。
BeanFactory
有一个实现类DefaultListableBeanFactory
我们要重点关注下。
这个类的类图如下图所示:
创建Bean对象
有了Bean的定义信息之后,Spring容器就可以开始创建对象了。
Bean对象的创建分为实例化和初始化两个步骤。
在我们日常使用Java创建对象的过程中,可能对这两个步骤没有分得这么清楚。
实例化就是给Bean对象开辟空间进行存储,并给对象里的属性赋初始值的过程。
初始化就是给对象做一些初始化的操作,例如填充属性、执行初始化方法。接下来我们就来聊聊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);
}
我们进一步点击SpringApplication
的run
方法,发现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容器。
那我们进一步看看这个方法里做了什么?
...
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
方法最后调用了容器applicationContext
的refresh
方法。
我们查看这个方法的注释,我们发现了,原来这个方法内部实例化了所有的单例Bean对象。
所以我们得出了结论,refreshContext
方法是对容器进行初始化操作的,至少包含了对容器内单例Bean对象的创建。
这不就是我们要找的方法吗?看来我们要找的Bean对象实例化的代码就在这个applicationContext
的refresh
方法里面。
我们进入refresh
方法看看。
我们发现这个方法有三个实现,不过看起来,AbstractApplicationContext
这个类比较像是我们要找的目标。
进入到AbstractApplicationContext
类的refresh
方法,我们发现了这样的代码。
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
注释中表示finishBeanFactoryInitialization
方法会实例化单例Bean对象,说明了这个是我们的目标。
我们进入finishBeanFactoryInitialization
方法看看,发现了实例化单例对象的其实调用的是Bean工厂beanFactory
的preInstantiateSingletons
方法。
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
这里的beanFactory
其实指的是DefaultListableBeanFactory
这个类。
进入到DefaultListableBeanFactory
的preInstantiateSingletons
方法,我们发现了我们距离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
的子类。
在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
方法实例化了对象。