为了以后不会忘记所学内容,特以记之,有疏忽错误的地方,还望指教
文章目录
1st. IoC容器与bean
首先贴上一段专业的描述:
IoC容器:容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。
bean:在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean,bean是一个由Spring IoC容器实例化、组装和管理的对象。
下面我来举个例子。IoC容器,就像搅拌机一样,而我们所说的bean,就是装在里面的食物。在使用时,我们首先要倒进去食物,再经过处理后得到搅拌机搅拌过的混合物,所以为了方便使用,我们就以map的数据结构来存储,并且给bean取了名字,在map中的key是beanName,而value是bean实例对象。这样我们才能找出想要吃的特定的那一种类美味的食物。
那我们要怎么拿到食物(bean)呢?比如通过context.getBean(class)之类的方式,可是想要获取容器里的食物(bean),我们肯定需要先倒进去食物,也就是创建对象:
- 使用new关键字
- 使用工厂
- 使用反射
那么在创建对象之前,我们需要对bean的属性信息进行定义,再将定义好的信息放入xml、json等格式的配置文件里,通过一种规范了配置文件格式的抽象接口传递给IoC容器中,这种抽象接口叫做BeanDefinitionReader,它的作用是读取 Spring 配置文件中的内容,将其转换为 IoC 容器内部的数据结构:BeanDefinition。
我们可以想象为将原料(bean的属性信息)用胶囊包起来(xml等配置文件)通过加工机器(BeanDefinitionReader)加工后后,胶囊变成了加工食品(BeanDefinition)被装入搅拌机(IOC容器)里。搅拌机再经过搅拌,制作出美味的饮品(bean对象)。
这里作为提醒,想要拓展一点相关知识,关于实例化和初始化的区分。
实例化:是指创建一个对象的过程。这个过程会在堆中开辟内存,将一些非静态的方法、变量存放在里面。在程序执行的过程中,可以创建多个对象,即多次实例化。属性是默认值。每次实例化都会开辟一块新的内存。
初始化:是完成程序执行前的准备工作。在这个阶段,**静态的(变量,方法,代码块)会被执行,同时会开辟一块存储空间用来存放静态的数据。**初始化只在类加载的时候执行一次。
现在,我们将BeanDefinition存入IoC容器中,就该创建对象了。在创建对象时,需要对bean先进行实例化,而后初始化,得到一个完整的bean对象。
2nd. 三个重要接口
在我们进行后续流程前,我们需要先探讨一下非常重要的三个接口:
BeanFactory接口、PostProcessor接口和Environment接口。
首先我们聊一聊spring中最熟悉的接口——BeanFactory接口,给出的介绍是进入spring bean容器的根接口。
The root interface for accessing a Spring bean container
当然咯,我们可以在BeanFactory中对对象进行创建。
先获取对象:
Class a = Class.forName(“完全限定名”);
Class a = 类.class;
Class a = 对象.getClass();
再实例化对象:
Object obj=a.newInstance();//适用于无参构造方法
或
Object obj=a.getDeclaredConstructor().newInstance();//适用于无参和有参构造方法
我们再来介绍PostProcessor接口,它分为BeanFactoryPostProcessor(Bean 工厂后置处理器)和BeanPostProcessor(Bean 后置处理器)。
BeanFactoryPostProcessor:
作为增强器,我们可以通过BeanFactoryPostProcessor对原来的BeanDefinition也就是bean的定义(配置元数据)进行拓展。
也就是说,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它。
要记住,BeanFactoryPostProcessor的作用域范围是容器级的,只和我们所使用的容器有关,如果我们在容器中定义一个BeanFactoryPostProcessor,它仅仅对容器中的bean进行后置处理,而不会对定义在另一个容器中的bean进行后置处理,即使两个容器都是在同一层次滴。
上图为BeanFactoryPostProcessor接口内的方法
上图为BeanFactoryPostProcessor示例
BeanPostProcessor:
上两张图为BeanPostProcessor接口定义的两种方法
如果我们想要改编实际的bean实例(例如从配置元数据创建的对象),那么我们就要使用BeanPostProcessor啦。
我们在bean实例化后使用,可以在初始化bean前后调用方法添加我们需要的功能逻辑(其中Before是实例化bean后、初始化bean前使用,而after是在实例化bean后、初始化bean后使用)
(图片来自马士兵教育)
第三个接口是Environment接口,主要管理应用程序的两个方面内容:profile和properties
profile相当于管理常见的环境,例如开发(dev)、测试(stg)等,在容器创建时会提前将系统的相关属性加载到StandardEnvironment对象中,方便后续使用。
而properties,是提供给用户方便的服务接口、以便于修改配置、解析配置。
3rd. xml在IoC容器中创建bean对象的流程
锵锵——
终于!我们可以开始介绍创建bean对象的流程啦,下面请跟紧我的步伐,先来简单看一下具体流程,详细细节我们会以后再聊~
首先我们假设已经获取到了xml路径,之后就可以根据路径做配置文件的解析及其他功能的实现。那么想要了解这一整个流程,我们最需要了解的就是refresh函数。
敲黑板!一定要对refresh函数很熟悉哦!
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1.准备刷新的上下文环境
prepareRefresh();
//Tell the subclass to refresh the internal bean factory.
//2. 初始化BeanFactory,并进行xml文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//Prepare the bean factory for use in this context.
//3. 进行各种功能填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//4.子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//5.激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6.注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候
registerBeanPostProcessors(beanFactory);
//Initialize message source for this context.
//7. 为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource();
//Initialize event multicaster for this context.
//8. 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9.留给子类来初始化其他的Bean
onRefresh();
// Check for listener beans and register them.
//10.在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
//Instantiate all remaining (non-lazy-init) singletons.
//11. 初始化剩下的单实例(非懒加载)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//12.完成刷新过程,通知生命周期处理器 lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
//Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
(1)初始化前的准备工作,例如对系统属性或环境变量进行准备与验证
(2)初始化BeanFactory,并读取xml文件。
如果我们不看获取xml路径的步骤,那么第一步就是初始化BeanFactory,譬如创业,首先需要有个公司,否则无法进行后续流程。
(3)对BeanFactory进行功能属性填充。
(4)子类覆盖方法做额外的处理。
(5)激活各种BeanFactory处理器。
(6)注册拦截bean创建的bean处理器,这里只是注册,真正的调用是在getBean的时候。
(7)为上下文初始化Message源,即对不同语言的消息体进行国际化处理。
(8)初始化应用消息广播器,并放入“applicationEventMulticaster”bean中。
(9)留给子类来初始化其他的bean。
(10)在所有注册的bean中查找listener bean,注册到消息广播器中。
(11)初始化剩下的单例(非惰性的)。
(12)完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。
一、环境准备
首先我们来看refresh中的第一个方法prepareRefresh:
// Prepare this context for refreshing.
//1.准备刷新的上下文环境
prepareRefresh();
prepareRefresh函数主要做准备刷新的工作,例如对系统属性及环境变量的初始化及验证。
下面我们进入到prepareRefresh函数中:
如上图,在prepareRefresh函数中,我们首先要设置容器启动时间、设置容器关闭为false、激活为true。
第一个方法是initPropertySources,用来初始化属性资源。
// Initialize any placeholder property sources in the context environment.
initPropertySources();
点进initPropertySources方法后会发现是空的,为了留给子类覆盖:
第二个方法是getEnvironment,用来验证需要的属性文件是否都已经放入环境中。
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
点进去getEnvironment方法后发现确实是验证创建环境。
我们来举个例子使用这两个方法。
假如有一个需求,工程在运行过程中需要某个设置(“pro”)是从系统环境变量中取得的,而如果用户没有再系统环境变量中配置这个参数,那么工程可能不会工作。
那么我们就可以重写initPropertySources方法,在其中添加getEnvironment().validateRequiredProperties(“pro”),这样在验证的时候也就是程序走到getEnvironment().validateRequiredProperties()代码时,如果系统没有检测到pro的环境变量,就会抛出异常。
二、加载BeanFactory
refresh的第二个方法obtainFreshBeanFactory,直译为获取BeanFactory,也就意味着,我们开始创建工厂啦!
//Tell the subclass to refresh the internal bean factory.
//2. 初始化BeanFactory,并进行xml文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
进入obtainFreshBeanFactory函数:
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
第一个方法是refreshBeanFactory,作用是初始化BeanFactory,并进行xml文件读取,将得到的BeanFactory记录在当前实体的属性中。再返回当前实体的BeanFactory属性。
进入refreshBeanFactory方法。
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//(1)
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//(2)
DefaultListableBeanFactory beanFactory = createBeanFactory();
//(3)
beanFactory.setSerializationId(getId());
//(4)
customizeBeanFactory(beanFactory);
//(5)
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
那么我们来详细分析以上步骤,下述序号与上图代码注释中的序号等同:
(1)我们首先判断有没有BeanFactory,有的话就销毁。假设我们这次是初建工厂,所以进行下一步~
(2)创建DefaultListableBeanFactory,这个可是容器的基础,一定要记住哦。
(3)在创建完工厂,我们需要设置工厂的序列化id,这样子其他人才能找到我们的工厂,也就是将之前xml文件读取到工厂中。
(4)这一步我们要定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖,比如设置@Autowired和@Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver。
(5)初始化DodumentReader,进行xml文件读取及解析。加载BeanDefinition。
三、功能扩展
refresh的第三个方法prepareBeanFactory:
//3. 进行各种功能填充
prepareBeanFactory(beanFactory);
进入prepareBeanFactory方法后,如下图,我们发现是对功能的补充。
refresh的第四个方法postProcessBeanFactory:
// Allows post-processing of the bean factory in context subclasses.
//4.子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
我们进入该方法,会发现里面没有内容,也就是说,postProcessBeanFactory是模板方法,需要的时候我们可以进行扩展实现。
四、BeanFactory的后处理
refresh的第五个方法invokeBeanFactoryPostProcessors:
// Invoke factory processors registered as beans in the context.
//5.激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
这个方法的作用就是调用激活所有注册的BeanFactoryPostProcessors。
refresh的第六个方法registerBeanPostProcessors:
// Register bean processors that intercept bean creation.
//6.注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候
registerBeanPostProcessors(beanFactory);
这个方法的作用是注册BeanPostProcessors,记住不是调用而是注册,跟上一个方法可不一样哦,本方法是现在不执行,等到实例化时再执行。
注意,Spring中大部分功能都是通过后处理器的方式进行扩展的,但是在BeanFactory中其实并没有实现后处理器的自动注册,所以在调用的时候如果没有进行手动注册其实是不能使用的。
refresh的第七个方法initMessageSource:
//Initialize message source for this context.
//7. 为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource();
这个方法的作用是作国际化处理。
refresh的第八个方法initApplicationEventMulticaster:
//Initialize event multicaster for this context.
//8. 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
这个方法的作用是初始化ApplicationEventMulticaster广播器。
这种方式比较简单,也就是考虑两种情况:
- 如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。
- 如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticaster。
refresh的第九个方法onRefresh:
// Initialize other special beans in specific context subclasses.
//9.留给子类来初始化其他的Bean
onRefresh();
该方法中是空的,留给子类。
refresh的第十个方法registerListeners:
// Check for listener beans and register them.
//10.在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
我们进入registerListeners方法中:
(1)硬编码方式注册的监听器处理
(2)配置文件注册的监听器处理
protected void registerListeners() {
// Register statically specified listeners first.
//(1)
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
//(2
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
五、初始化非懒加载单例
refresh的第十一个方法finishBeanFactoryInitialization:
//Instantiate all remaining (non-lazy-init) singletons.
//11. 初始化剩下的单实例(非懒加载)
finishBeanFactoryInitialization(beanFactory);
首先单例模式指的是,确保在任何时候,该类只有唯一一个实例。
单例的创建有两种方式:
- 非懒加载(非延迟加载):不管什么时候使用,都要先提前创建实例。
- 懒加载(延迟加载):等到真正要使用的时候才去创建实例,不用时不要去创建。
我们进入finishBeanFactoryInitialization方法中,其中有两个方法我们来解释一下。
第一个方法是beanFactory.freezeConfiguration():
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
作用是冻结所有的bean定义,说明注册的bean定义将不被修改或进行任何进一步的处理。
第二个方法是beanFactory.preInstantiateSingletons():
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
作用是实例化所有剩下的非懒加载的单例。
ApplicationContext实现的默认行为就是在启动时将所有单例的bean都提前实例化。这意味着作为初始化过程中的一部分,ApplicationContext实例会创建并配置所有单例bean,这样在配置中的任何错误就会立刻发现。而这个实例化的过程就是在finishBeanFactoryInitialization中完成的。
我们进入preInstantiateSingletons方法中看一下:
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
//(1)
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
//(2)
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//(3)
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
//(4)
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
(1)我们将bean定义里的名字放入list集合中,从头循环
(2)我们获取bean定义信息内容,判断bean是否是抽象、单例、懒加载的
此处插一嘴,关于BeanFactory和FactoryBean的区分:
BeanFactory是一个bean工厂,FactoryBean是一个java bean。
BeanFactory是Ioc容器的根接口,Spring不允许我们直接操作BeanFactory bean工厂,就为我们提供了ApplicationContext这个接口,继承自BeanFactory接口,ApplicationContext包含BeanFactory的所有功能。
而FactoryBean是spirng提供的⼯⼚bean的⼀个接⼝,FactoryBean接⼝提供三个⽅法,⽤来创建对象
(3)判断name的bean对象是否属于FactoryBean
(4)创建bean之前我们要先获取bean:
进入后看到下图doGetBean方法
我们再深入进这个方法:
由于代码太多,我就不贴在这里啦,我们主要看比较重要的一些方法就好。
(4.1)获取单例对象,因为我们是第一次创建,所以这个地方为null。
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
(4.2)获取依赖属性
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
(4.3)我们开始创建bean
if (mbd.isSingleton()) {
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);
}
我们再深入进createBean(beanName, mbd, args)中。
(4.3.1)找到doCreateBean方法。
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
进入doCreateBean(beanName, mbdToUse, args)方法。
(4.3.1.1)
发现createBeanInstance方法,终于开始实例化bean!我们继续进入这个方法。
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
(4.3.1.1.1)
看到这一行代码,我们需要先获取构造器,才能实例化,于是就要验证这个构造器是否是我们需要的那个。
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
(4.3.1)我们回到doCreateBean方法中去,找到下面populateBean方法,把xml配置文件中的属性填充给bean。
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
我们再执行aware方法。
invokeAwareMethods(beanName, bean);
然后就可以执行applyBeanPostProcessorsBeforeInitialization方法。
继续invokeInitMethods方法初始化bean。
最后执行applyBeanPostProcessorsAfterInitialization方法。
返回当前bean对象。
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
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()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
至此,我们在IoC容器里创建bean对象的过程就结束啦。
最后执行applyBeanPostProcessorsAfterInitialization方法。
返回当前bean对象。
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
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()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
至此,我们在IoC容器里创建bean对象的过程就结束啦。