详实明了的IOC容器的介绍,启动流程以及Bean的实例化和依赖注入
前言
今天我们来认识一下Spring IOC容器,本文主要介绍SpringIOC容器的核心要点以及其启动流程和实例化流程。
项目环境
Springframework 4.3.12
核心要点
Spring IOC是什么?他有什么作用呢?我们通过了解学习,Spring IOC是一个容器,用于生成和管理Bean的实例,以及实例之间的依赖关系,然后注入相关的依赖。这里我们可以把IOC容器想象成一个餐馆。我们去餐馆点菜的话,不需要关心菜的生成过程,不需要关心菜的原材料从哪里来。我们只需要最终做好的菜。这里的菜就是我们的需要的Bean。不同的菜对应不同的Bean。没有IOC 容器的情况下,如果需要一个Bean的话,就需要自己来new一个对象的实例,比如A类依赖了B类,那么就需要在A类中new一个B类的实例对象,这就好像我们要自己在家动手做菜一样。有了IOC容器之后,如果A类依赖B类,只需要通过IOC容器帮我们创建A类的实例和B类的实例,然后IOC容器会将B类的实例注入到A类中。这就很像餐馆把菜做好之后送给我们一样。既然IOC容器这么6,那么他是如何实现这一切的呢?
还是回到餐馆那个例子,做菜的话就需要与原材料和菜谱,同样的IOC容器想要管理各个业务对象以及他们之间的依赖关系,就需要通过某种途径来记录和管理这些信息,而BeanDefinition对象就承担了这个责任。IOC容器中每一个Bean都会有一个对应的BeanDefinition实例,该实例负责保存bean对象的所有必要信息,包括Bean对象的class类型,是否是抽象类,构造方法和参数,以及其他属性等,这里的BeanDefinition就相当于原材料。而BeanDefinitionRegistry对象和BeanFactory对象就相当于菜谱,告诉我们如何将原材料加工成相应的菜肴。
下面我们就来看看这些比较核心的类和接口。
类 | 作用 |
---|---|
BeanFactory | BeanFactory接口主要包括了getBean,containsBean,getAllases等方法,作用是操作Bean |
BeanDefinitionRegistry | BeanDefinitionRegistry接口抽象出了Bean的注册逻辑,其主要包括了registerBeanDefinition,removeBeanDefinition,getBeanDefinition等方法 |
ConfigurableListableBeanFactory | ConfigurableListableBeanFactory接口包括了getBeanDefinition等方法,可以获取BeanDefinition实例 |
DefaultListableBeanFactory | DefaultListableBeanFactory类同时实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,说明其承担了Bean的注册和管理工作 |
BeanDefinition | BeanDefinition接口是用来存储Bean对象的必要信息,包括Bean对象的class类型,是否是抽象类,构造方法和参数,依赖关系,以及其他属性等 |
PropertyValue | 这个类就是具体存放Bean对象的属性信息以及其依赖信息 |
认识上面的几个核心接口和类,对我们下面看Bean的启动过程和实例化过程有很大的帮助。
需要说明的是,在Spring中,ApplicationContext是IOC容器的承载体,而BeanFactory是操作这个容器的工具,两者关系紧密,相互协作,refresh方法实现了ApplicationContext和BeanFactory相互协作的过程,不同之处主要在于子类 AbstractRefreshableApplicationContext 和 GenericApplicationContext 中实现,两者使用的 BeanFactory 都为 DefaultListableBeanFactory,它构建在BeanFactory之 上,属于更⾼级的容器,除了具有BeanFactory的所有能⼒之外,还提供对事件监听机制以及国际化的⽀持等。它管理的bean,在容器启动 时全部完成初始化和依赖注⼊操作。
IOC容器的启动过程
介绍完了IOC容器的核心类和要点,接下来我们看看IOC容器的启动过程,其启动过程主要有如下三个步骤:
1. 资源定位,找到配置文件
这里定位资源有两种方式,一种是通过ClassPathXmlApplicationContext
类来解析Spring的配置文件的形式,就是通过配置文件来定义Bean的情况,另外,一种情况就是通过注解的方式来定义Bean的情况,这种情况是通过AnnotationConfigApplicationContext
类解析的,主要是扫描项目的classPath下定义的注解。下面我们首先介绍下通过ClassPathXmlApplicationContext
。这个类的核心作用是作为一个解析Xml的入口,其调用链是: ClassPathXmlApplicationContext
类的构造器
------>AbstractApplicationContext
类的refresh
方法
----->调用AbstractRefreshableApplicationContext
类的refreshBeanFactory
方法
---->XmlWebApplicationContext
类的loadBeanDefinitions
方法
----> AbstractBeanDefinitionReader
类的loadBeanDefinitions
方法
---->XmlBeanDefinitionReader
类的loadBeanDefinitions
方法
---->XmlBeanDefinitionReader
类的doLoadBeanDefinitions
方法
---->XmlBeanDefinitionReader
类的registerBeanDefinitions
方法
---->DefaultBeanDefinitionDocumentReader
类的registerBeanDefinitions
方法
调用层次很深,我们就直接跳到核心的方法来看。下面我们就来看看registerBeanDefinitions
方法
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//读取XML文件
Element root = doc.getDocumentElement();
//载入并注册BeanDefinition
doRegisterBeanDefinitions(root);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后,registerBeanDefinitions
方法只是读取到根节点root之后,就另外一个核心方法doRegisterBeanDefinitions
方法,然后,doRegisterBeanDefinitions
方法又把逻辑转给了parseBeanDefinitions
方法,这个parseBeanDefinitions
方法首先获取所有的子节点 ,然后遍历解析子节点,载入BeanDefinition
又交给了parseDefaultElement
方法和parseCustomElement
方法。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) { //获取子节点 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //解析节点 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } }
}
else { delegate.parseCustomElement(root);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2.BeanDefinition的载入和解析,将配置文件解析成BeanDefiniton
说完了配置文件的解析之后,接下来,我们来看看BeanDefinition
的载入和解析。我们直接找到parseDefaultElement
方法。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//省略部分非核心代码
//如果节点是bean节点,说明是一个Bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这个方法按照节点名,调用不同的处理方法,在此处我们只看节点为bean时调用的方法processBeanDefinition
方法。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance.(注册BeanDefinition) BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
我们重点看BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
这个方法,这个方法才是真正的将传入BeanDefinitionRegistry
类,载入并解析BeanDefinition,然后对BeanDefinition进行注册。
3. BeanDefinition的注册,将BeanDefinition向Map中注册beanDefinitionMap
接下来就到了我们的重头戏,注册BeanDefinition到beanDefinitionMap
中,其中key就是Bean的id,其中beanDefinitionMap
是一个定义在DefaultListableBeanFactory
类中全局的线程安全的map,用于存放解析到的BeanDefinition
。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
- 1
让我们来看看registerBeanDefinition
方法吧,这个方法核心的步骤有两步:
- 根据传入的beanName检查beanDefinition是否存在,如果存在就是一系列的校验,主要是保证BeanDefinition的单例性,就是说IOC容器中每个Bean的实例时单例的。
- 将传入的
beanDefinition
实例放到beanDefinitionMap
中。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //加锁,保证线程安全 synchronized (this.beanDefinitionMap) { // 将beanDefinition值设置到beanDefinitionMap中 this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase,将beanDefinition值设置到beanDefinitionMap中 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
小结,至此,我们对IOC容器的初始化过程就解析完了,其实其初始化过程还是比较简单的,只是Spring的代码结构比较深,核心代码不好找。
Bean的实例化和依赖注入
说完了IOC容器的初始化过程,接下来,我们来看看IOC容器的实例化过程。经过上一个阶段,所有Bean定义都通过BeanDefinition
的方式注册到了BeanDefinitionRegistry
中。当某个请求通过容器的getBean
方法请求某个对象,或者因为依赖关系容器需要隐式的调用getBean
方法时,就会触发第二阶段的活动,IOC容器首先检查所请求的对象是否已经实例化完成,如果没有,则会根据注册的BeanDefinition
所提供的信息实例化请求对象。并为其注入依赖,当该对象装配完成后,容器会立即返回给请求方法。
Bean的实例化
让我们从前面提到的getBean
方法说起,这里的调用链如下:
AbstractBeanFactory
类的getBean
方法
----->AbstractBeanFactory
类的doGetBean
方法
----->AbstractBeanFactory
类的createBean
方法
------>AbstractAutowireCapableBeanFactory
类的doCreateBean
方法(这个方法主要是)
创建Bean的核心逻辑就在AbstractAutowireCapableBeanFactory
类的doCreateBean
方法中:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean(实例化Bean)
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) { //如果是单例Bean,首先清理FactoryBean缓存 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) { //使用特定的策略实例化Bean,如果工厂方法、构造器等,将BeanDefinition转换为BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
} final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
// Allow post-processors to modify the merged bean definition. 允许MergedBeanDefinitionPostProcessor修改BeanDefinition 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.
//需要提前暴露Bean的条件:单例&&允许循环依赖&&当前Bean正在创建中,检测到了循环依赖 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"); } //对于需要提前暴露的Bean,以其ObjectFactory的形式放入singletonFactories中,以解决循环依赖的问题 //ObjectFactory所创建的Bean由getEarlyBeanReference()方法指定 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
// Initialize the bean instance. Bean的初始化
Object exposedObject = bean; try { //对Bean进行属性的填充。此外,如果依赖了其他Bean,则会在这里注入依赖 populateBean(beanName, mbd, instanceWrapper); //执行Bean的初始化方法,如配置的init-method等 exposedObject = initializeBean(beanName, exposedObject, mbd); }
//循环依赖检查 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { //省略部分代码 throw new BeanCurrentlyInCreationException(); } } } } try { //注册Bean的销毁方法,如destroy-method registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject;
} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
从doCreateBean方法我们可以看出Bean生命周期的一个完整过程,这个方法里面的流程很长,知识点很多。下面我们就对流程进行具体的分析。
流程分析
实例化Bean
实例化Bean的逻辑是在SimpleInstantiationStrategy
类的instantiate
方法中实现的。Bean的实例化其实就是将指定的BeanDefinition转换成BeanWrapper,然后通过指定构造器和默认无参构造器,CGLB动态代理等方式来实例化Bean,这时实例化后的Bean是一个刚实例化好的,属性未赋值的空Bean。
@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
//省略部分代码
// Don't override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) { //通过构造器实例化Bean return BeanUtils.instantiateClass(constructorToUse);
}
else { // Must generate CGLIB subclass.(通过CGLIB的方式生成Bean) return instantiateWithMethodInjection(bd, beanName, owner);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Bean的依赖注入(属性注入)
说完了Bean的实例化,接下来我们来说下Bean的依赖注入。属性注入必须用到PropertyValue类,这个类保存了Bean的所有属性和依赖信息。
依赖注入的调用流程是AbstractAutowireCapableBeanFactory
类的applyPropertyValues
方法。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { //类型转换接口 TypeConverter converter = getCustomTypeConverter();
if (converter == null) { converter = bw;
}
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
String propertyName = pv.getName(); //对于ref来说就是beanName,对于value 来说就是value Object originalValue = pv.getValue(); Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); }
try { //设置依赖属性 bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
} private Object convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
if (converter instanceof BeanWrapperImpl) { return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
其中TypeConverter 类型转化接口,将传入的值转化为其需要的类型。
SimpleTypeCoverter 是TypeConverter接口的一个实现。其依赖于java.beans中的PropertyEditor,其类似于java GUI中的编程,例如:拖拽一个button, 然后,设置其颜色,长度,宽度,这些都属于button的属性,在java.beans中将这些抽象成了一个PropertyEditor 接口。 setAsText(), 例如button 的高度,值是什么跟属性的类型密切相关。
总结
本文主要介绍了IOC容器的核心概念,以及其启动过程。然后,就是介绍了Bean的实例化过程,熟悉IOC容器我们需要先了解清楚其核心的几个接口,例如:BeanFactory接口,BeanDefinitionRegistry接口等。IOC容器的启动过程无非就是解析配置文件,将属性值存放到BeanDefinition中。Bean的实例化是通过反射或者CGLIB的方式来的。Bean中的属性是存放在PropertyValue中。
文章来源: feige.blog.csdn.net,作者:码农飞哥,版权归原作者所有,如需转载,请联系作者。
原文链接:feige.blog.csdn.net/article/details/105854424
- 点赞
- 收藏
- 关注作者
评论(0)