博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于XML的IOC容器初始化以及循环依赖问题——Spring中的refresh()方法
阅读量:3913 次
发布时间:2019-05-23

本文共 37996 字,大约阅读时间需要 126 分钟。

基于XML的IOC容器初始化以及循环依赖问题——Spring中的refresh()方法

文章目录

1,找到入口

我们首先找到测试的入口来一探究竟,相信下面这段代码大家都写过:

@Testpublic void test01() {
// 入口 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); TestService bean = applicationContext.getBean(TestService.class); bean.test();}

我们先来看一下 ClassPathXmlApplicationContext的所处的体系:

在这里插入图片描述

ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 Bean 的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring 应用提供了一个共享的 Bean 定义环境。

首先根据上面的代码点进来:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {
configLocation}, true, null);}

其实际调用的构造函数为:

public ClassPathXmlApplicationContext(      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)      throws BeansException {
super(parent);// 位置1 setConfigLocations(configLocations); if (refresh) {
refresh(); }}

实际上,xml应用中正是通过这个构造方法进行的IOC初始化过程。

2,准备工作

通过分析ClassPathXmlApplicationContext的源代码可以知道,在创建ClassPathXmlApplicationContext容器时,构造方法首先做了以下两项工作

2.1. 配置Bean资源加载器

从上面代码的位置1看进去,结合类图看一下以下代码,可以发现其层层调用父类的构造方法,直到AbstractApplicationContext。

// AbstractXmlApplicationContext中public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);}// AbstractRefreshableConfigApplicationContext中public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);}// AbstractRefreshableApplicationContextpublic AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);}// AbstractApplicationContextpublic AbstractApplicationContext(@Nullable ApplicationContext parent) {
this(); // 位置1 setParent(parent);}

接下来我们点进去上面位置1的代码,如下所示

public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver(); }// 设置资源加载器protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);//位置1}// AbstractApplicationContext这里其实也是一个资源加载器public abstract class AbstractApplicationContext extends DefaultResourceLoader ....

这里可以看到,位置1处, AbstractApplicationContext将自己设置为了Bean资源加载器并返回。

因此ClassPathXmlApplicationContext中super(parent);这行代码的目的就是为容器设置好Bean资源加载器(AbstractApplicationContext)。

2.2 设置配置信息

有了加载器,那么肯定就还需要加载器所加载的配置文件信息,setConfigLocations(configLocations);这行代码,目的就是对Bean配置信息进行定位。

// AbstractRefreshableConfigApplicationContext中的方法public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim(); } } else {
this.configLocations = null; }}

通过以上代码我们也可以看出,我们即可以使用一个字符串来配置多个Spring Bean配置信息,也可以使用字符串数组,即下面两种方式都可以:

ClassPathResource res = new ClassPathResource("a.xml,b.xml");ClassPathResource res =new ClassPathResource(new String[]{
"a.xml","b.xml"});

3,开始启动

SpringIOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh()函数启动整个 IOC 容器对 Bean 定义的载入过程,现在我们来详细看看 refresh()中的逻辑处理:

@Overridepublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识 prepareRefresh(); // 载入 位置1 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等 prepareBeanFactory(beanFactory); ... }}

我们说的载入过程,其实就是从obtainFreshBeanFactory();(位置1)这个地方开始的

refresh()方法的主要作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和 关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。它类似于对 IOC 容器的重启,在新建立 好的容器中对容器进行初始化,对 Bean 配置资源进行载入。

4,载入

obtainFreshBeanFactory()方法调用子类容器【AbstractApplicationContext】的 refreshBeanFactory()方法,启动容器载入 Bean 配置信息的过程,代码如下:

// AbstractApplicationContextprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory(); return getBeanFactory();}// 抽象方法protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法,方法的源码如下:

@Overrideprotected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
// 位置1 destroyBeans(); closeBeanFactory(); } try {
DefaultListableBeanFactory beanFactory = createBeanFactory();// 位置2 beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory);// 位置3 this.beanFactory = beanFactory; } catch (IOException ex) {
... }}

在这个方法中,首先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory(位置1),接着创建DefaultListableBeanFactory(位置2),并调用loadBeanDefinitions(beanFactory)方法装载bean定义(位置3)。重点是位置3的装载bean定义的调用。

5,配置资源读取器

我们从4步骤中的位置3点进去:

// AbstractRefreshableApplicationContext 抽象方法protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)			throws BeansException, IOException;// AbstractXmlApplicationContext具体实现@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个XmlBeanDefinitionReader读取Bean配置资源 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 位置1 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader);}

该步骤的目的主要是使用我们定义的XML资源读取器(XmlBeanDefinitionReader)去加载配置资源。

6,处理文件并加载

从上步骤代码的位置1点进去,最终我们可以看到如下代码:

public int loadBeanDefinitions(String location, @Nullable Set
actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) {
throw new BeanDefinitionStoreException("..."); } if (resourceLoader instanceof ResourcePatternResolver) {
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);// 位置1 int count = loadBeanDefinitions(resources); // 位置2 ... }}

在该处我们可以看到程序就做了两件事:

  1. 位置1调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源

    其实就是 XmlBeanDefinitionReader 通过调用ClassPathXmlApplicationContext的父类DefaultResourceLoader的getResource()方法获取要加载的资源。

  2. 位置2加载bean定义

    在下个步骤中详细解释

总结一下这个流程的目的:寻找要加载资源的位置并加载。

7,读取配置内容

继续上个环节的位置2处的代码:loadBeanDefinitions(resources)。

该处的主要目的是加载Bean定义,通过点进去几层,我们最终来到了XmlBeanDefinitionsReader的这个位置:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)      throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);// 位置1 int count = registerBeanDefinitions(doc, resource);// 位置2 if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) {
...}}

这一步的目的是:将配置文件解析为文档对象注册Bean定义

  • 解析文档对象

    位置1看进去,最终会来到 DefaultDocumentLoaderloadDocument() 方法:

    @Overridepublic Document loadDocument(InputSource inputSource, EntityResolver entityResolver,      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) {
    logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource);}

    通过这个操作,Spring就完成了将配置文件转换成为了Document对象的过程。

  • 注册Bean定义

    接下来的步骤将会解释如何将Document对象解析为SpringIOC容器管理的Bean对象,并注册到容器中的。

8,创建 BeanDefinitionDocumentReader 解析资源

XmlBeanDefinitionReader 类中的 doLoadBeanDIfinition()(上步骤的)方法是从特定的XML文件中实际载入Bean配置资源的方法,该方法在载入Bean配置资源之后将其转换为Document对象,接下来调用registerBeanDefinitions() 启动SpringIOC容器对Bean定义的解析过程,registerBeanDefinitions()方法的源码如下:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader解析doc BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); // 具体的解析实现过程 位置1 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;}

Bean配置资源的载入解析分为以下两个过程:

  • 首先通过调用xml解析器将Bean配置信息转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析

  • 其次,在完成XML解析之后,按照SpringBean的定义规则对Document对象进行解析,其解析过程是在接口 BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader 中实现位置1)。

9,解析Document对象

BeanDefinitionDocumentReader 接口通过 registerBeanDefinitions() 方法调用其实现类 DefaultBeanDefinitionDocumentReader 对 Document 对象进行解析,代码如下:

// 解析Document对象@Overridepublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement());}protected void doRegisterBeanDefinitions(Element root) {
//具体的解析过程由 BeanDefinitionParserDelegate 实现, //BeanDefinitionParserDelegate 中定义了 Spring Bean 定义 XML 文件的各种元素 BeanDefinitionParserDelegate parent = this.delegate; //创建 BeanDefinitionParserDelegate,用于完成真正的解析过程 this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
... } return; } } } // 在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性 preProcessXml(root); // 从Document的根元素开始解析Bean定义的Document对象 位置1 parseBeanDefinitions(root, this.delegate); // 在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性 postProcessXml(root); this.delegate = parent;}

虽然解析的过程是由DefaultBeanDefinitionDocumentReader 调用,但是实际的解析过程则是由 BeanDefinitionParserDelegate 来完成(位置1)。

10,开始解析

接上步骤中的位置1:由 BeanDefinitionParserDelegate 来完成Document的解析过程,代码如下:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// 获取Bean定义的Document对象根元素的所有字节点 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)) {
// //使用 Spring 的 Bean 规则解析元素节点 位置1 parseDefaultElement(ele, delegate); } else {
delegate.parseCustomElement(ele); } } } } else {
delegate.parseCustomElement(root); }}

位置1进去,代码如下:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果元素时
导入元素,进行导入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele); } // 如果元素时
导入元素,进行别名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele); } //元素节点既不是导入元素,也不是别名元素,即普通的
元素, //按照 Spring 的 Bean 规则解析元素 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);// 位置1 } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele); }}

可以看到,代码中根据标签类型,对标签内的元素进行解析。下面将以标签为例,一起来看一下是如何载入的。

11,加载Bean

上步骤的位置1点进来,记住这个位置,稍后我们还会回来:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 第一行 位置1 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); ...}

我们这里只看该方法的第一行:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);}

再进来就是解析bean标签的核心方法了,这个方法主要处理bean元素的id,nam和别名属性:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取bean元素中的id属性 String id = ele.getAttribute(ID_ATTRIBUTE); // 读取bean元素中的属性值 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 新建集合存储别名,如果配置了别名就尝试获取 List
aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; // 如果 bean元素中没有设置id属性时,将别名中第一个值赋值给beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0); if (logger.isTraceEnabled()) {
...} } if (containingBean == null) {
// 检查bean元素所配置id、name或别名是否重复 checkNameUniqueness(beanName, aliases, ele); } // 详细对bean元素中配置的bean定义进行解析的地方 位置1 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果
元素中没有配置 id、别名或者 name,且没有包含子元素 //
元素,为解析的 Bean 生成一个唯一 beanName 并注册 beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else {
//如果
元素中没有配置 id、别名或者 name,且包含了子元素 //
元素,为解析的 Bean 使用别名向 IOC 容器注册 beanName = this.readerContext.generateBeanName(beanDefinition); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName); } } if (logger.isTraceEnabled()) {
... } } catch (Exception ex) {
error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null;}

截止到这里,Spring已经完成了对id,name属性的解析,bean中详细内容的解析就交由 parseBeanDefinitionElement()这个方法来完成位置1),接下来我们一起看一下这个方法的源码:

@Nullablepublic AbstractBeanDefinition parseBeanDefinitionElement(      Element ele, String beanName, @Nullable BeanDefinition containingBean) {
// 记录要解析的bean this.parseState.push(new BeanEntry(beanName)); // 记录 class 属性的内容 如:com.xx.pojo.Person String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } // 如果bean标签中配置了parent属性,那么就记录 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE); } try {
/* 根据bean元素配置的class名称和parent属性值创建BeanDefinition 为载入Bean定义信息做准备*/ AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 对当前的bean元祖中配置的一些属性进行解析和设置 // 如配置的单例属性(Singleton)等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // 为bean元素解析的bean设置 description 标签信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 对bean元素的元信息属性解析 parseMetaElements(ele, bd); // 解析lookup-method属性和replaced-method parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析构造方法 parseConstructorArgElements(ele, bd); // 解析property标签 parsePropertyElements(ele, bd);// 位置1 // 解析bean元素的 qualifier 标签 parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) {
... } finally {
this.parseState.pop(); } return null;}

只要对配置文件比较熟悉,通过以上代码的分析,就会明白我们再Spring配置文件中bean元素中配置的属性就是通过该方法解析和设置到Bean中去的。

注意在解析bean元素过程中并没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefiniton,将bean元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象

我们再使用bean标签进行配置时,使用最多的就是property,接下来我们一起看一下property标签是如何配置的(代码位置1)。

12,载入property标签元素

BeanDefinitionParserDelegate 在解析bean时调用parsePropertyElements()方法来完成对property标签的解析(上步骤代码位置1)。源码如下:

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取bean元素中所有子元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i); // 如果子元素是property元素,则调用解析方法解析 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);// 位置1 } }}

这里可以看到,加载流程为:首先获取该bean标签中所有的元素,如果元素是property,则调用解析方法解析该标签:

// 解析 propertypublic void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } // 解析获取property的值 Object val = parsePropertyValue(ele, bd, propertyName); // 根据property的名字和值创建property实例 PropertyValue pv = new PropertyValue(propertyName, val); // 解析property标签内的属性 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally {
this.parseState.pop(); }}

这里就不详细往里看了,时间有限…

4-12步总结:

经过4-12步的介绍,我们有了一个清晰的脉络:

  1. 首先bean.xml文件会首先被我们所配置的资源加载器加载【ClassPathXmlApplicationContext】
  2. 接着由我们配置的资源读取器【XmlBeanDefinitionReader】,加载我们所配置的配置文件
  3. 由【DefaultBeanDefinitionDocumentReader 】将我们配置的资源解析为Document对象
  4. 由【BeanDefinitionParserDelegate 】来完成Document的解析过程
  5. 最终我们将xml中的配置信息转换成了Spring IOC所识别的数据结构——BeanDefinition

通过这几步对配置资源的解析后,IOC容器大致完成了管理bean对象的准备工作,即初始化过程,但是最重要的依赖注入等操作还没有发生,现在在IOC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器中注册Bean的定义信息才能全部完成IOC容器的初始化过程。

13,让我们回到11步

从上面的流程中我们知道,Spring在11-12步中,完成了对配置文件的加载工作,并将Bean配置信息成功转换为了BeanDefinition,接下来 BeanDefinitionParserDelegate 将分封装了 BeanDefinition 的BeanDefinitionHold对象返回

// DefaultBeanDefinitionDocumentReader中的processBeanDefinition方法protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 11步中的位置1 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try {
// 向容器中注册解析的 Bean 位置1 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) {
... } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}

来看代码的位置1,由 BeanDefinitionReaderUtils 的 registerBeanDefinition() 方法向IOC容器中注册解析的 Bean。

14,注册Bean

上步骤中的位置1点进去,

public static void registerBeanDefinition(    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)    throws BeanDefinitionStoreException {
// 获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); // 向容器中注册 位置1 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果解析的BeanDefinition有别名,则注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias); } }}

当 BeanDefinitionReaderUtils 调用 registerBeanDefinition() 方法时,真正完成注册操作的是 DefaultListableBeanFactory 接下来我们一起看进去(位置1):

// DefaultListableBeanFactory 解析注册Beanpublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)      throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(...); } } BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (logger.isInfoEnabled()) {
... } } else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
... } } else {
if (logger.isTraceEnabled()) {
... } } this.beanDefinitionMap.put(beanName, beanDefinition); } else {
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition); List
updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else {
// 位置1 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName); } else if (isConfigurationFrozen()) {
clearByTypeCache(); }}

这个方法很长,简而言之就是做了一件事: 使用一个ConcurrentHashMap(this.beanDefinitionMap)的集合对象存放IOC容器解析的BeanDefinition(位置1)。

小总结:

至此,Bean配置信息中配置的Bean就被解析完成,且注册到IOC容器中,被容器管理起来,真正完成了IOC容器初始化所做的全部工作。现在IOC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并可以被检索,IOC容器的作用就是对这些注册的Bean定义信息进行维护和处理。这些注册的Bean定义信息是IOC容器控制反转的基础,正式有了这些注册的数据,容器才可以进行依赖注入。

15,Bean初始化之前的准备工作

描述请看代码

@Overridepublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 准备工作 prepareRefresh(); // 将xml解析为 BeanDefinition 获取BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /* 准备工作:设置BeanFactory的类加载器、支持表达式解析器、 添加部分BeanFactoryProcessor等 */ prepareBeanFactory(beanFactory); try {
// 模板方法:准备工作完成后的后置处理工作 postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // 调用Bean工厂后置处理器 invokeBeanFactoryPostProcessors(beanFactory); // 注册Bean的后置处理器 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 初始化MessageSource组件(做国际化功能) initMessageSource(); // 初始化事件派发器 initApplicationEventMulticaster(); onRefresh(); // 注册监听器 registerListeners(); // 初始化单例Bean 位置1 finishBeanFactoryInitialization(beanFactory); finishRefresh(); } catch (BeansException ex) {
if (logger.isWarnEnabled()) {
... } destroyBeans(); cancelRefresh(ex); throw ex; } finally {
resetCommonCaches(); contextRefresh.end(); } }}

16,初始化单实例Bean

我们来一起看一下上步骤中的位置1,在finishBeanFactoryInitialization(beanFactory)方法中,会完成初始化单例Bean的工作,我们来一起看一下源码:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
... // 实例化所有剩余的单例Bean 位置1 beanFactory.preInstantiateSingletons();}

这里我们只看最后一行代码:beanFactory.preInstantiateSingletons();

该方法使用DefaultListableBeanFactory的preInstantiateSingletons()方法进行Bean的初始化工作:

@Overridepublic void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this); } List
beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) {
FactoryBean
factory = (FactoryBean
) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged( (PrivilegedAction
) ((SmartFactoryBean
) factory)::isEagerInit, getAccessControlContext()); } else {
isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean
) factory).isEagerInit()); } if (isEagerInit) {
getBean(beanName); } } } else {
getBean(beanName);//位置1 } } ...}

可以看到在该方法中获取了Bean的定义信息并加载bean:

@Overridepublic Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);}// getBean调用doGetBeanprotected
T doGetBean( String name, @Nullable Class
requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = transformedBeanName(name); Object beanInstance; // 1,先获取缓存中保存的单实例Bean,如果能获取到 // 说明这个Bean之前被创建过 Object sharedInstance = getSingleton(beanName); // 2,缓存中没有拿到 if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("..."); } else {
logger.trace("..."); } } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName); } BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType); } else {
return (T) parentBeanFactory.getBean(nameToLookup); } } // 3,标记当前Bean已经被创建 if (!typeCheckOnly) {
markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString); } // 4,获取Bean的定义信息 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); //,5,获取当前Bean依赖的其他Bean // 如果有依赖其他bean,则通过getBean获取 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new ... } registerDependentBean(dep, beanName); try {
// 如果有则通过getBean获取 getBean(dep); } catch (NoSuchBeanDefinitionException ex) {
... } } } // 6, 启动创建Bean的流程 if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args); } catch (BeansException ex) {
destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance. Object prototypeInstance = null; try {
beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally {
afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else {
String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) {
throw new ... } Scope scope = this.scopes.get(scopeName); if (scope == null) {
throw new ... } try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName); try {
return createBean(beanName, mbd, args); } finally {
afterPrototypeCreation(beanName); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) {
... } finally {
beanCreation.end(); } } return adaptBeanInstance(name, beanInstance, requiredType);}

通过上面这个方法我们可以得到的信息是:在进行Bean的初始化过程中,首先会使用 getSingleton(beanName);方法尝试从缓存中获取Bean,代码如下:

@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) {
ObjectFactory
singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) {
singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject;}// 一级缓存private final Map
singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存private final Map
earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存private final Map
> singletonFactories = new HashMap<>(16);

如果没有获取到则会将当前Bean已创建状态,然后执行创建Bean的操作。

17,创建Bean

从createBean(beanName, mbd, args)方法看进来:

@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)      throws BeanCreationException {
... Object beanInstance = doCreateBean(beanName, mbdToUse, args); ...}

我们来只看这一行的doCreateBean()方法:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)      throws BeanCreationException {
... // 位置1(初始化的第一步) instanceWrapper = createBeanInstance(beanName, mbd, args); ... // 位置2 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... // 位置3 populateBean(beanName, mbd, instanceWrapper); // 位置4 exposedObject = initializeBean(beanName, exposedObject, mbd); ... ... return exposedObject;}

这里只留下几个关键代码进行介绍:

  • 位置1:这是初始化的第一步,就是将Bean创建出来,需要注意的是,此处只是将Bean创建了出来
  • **位置2(重点!!!)**这个地方我们在下个步骤解释
  • 位置3:populateBean(beanName, mbd, instanceWrapper) 完成Bean属性赋值,主要是配置前、后置处理器,并为属性利用setter等进行赋值
  • 位置4:initializeBean(beanName, exposedObject, mbd) 执行bean的初始化
    1. 执行Bean实现的Aware接口的方法
    2. 执行后置处理器初始化之前的方法
    3. 执行初始化方法
    4. 执行后置处理器初始化之后的方法
    5. 注册bean的销毁方法

18,循环依赖

上步骤中的位置2是解决循环依赖的关键,我们来一起看一下这个方法的内容:

protected void addSingletonFactory(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 加到三级缓存中 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }}

这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化

注:该步骤参考博客:https://blog.csdn.net/u010853261/article/details/77940767

19,创建完成

创建完Bean之后将会调用getSingleton(String, ObjectFactory<?>)方法获取单实例Bean,并在该方法中最终调用addSingleton方法将该Bean添加到缓存(singletonObjects)中:

protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存中 this.singletonObjects.put(beanName, singletonObject); // 从二级缓存中删除 this.singletonFactories.remove(beanName); // 从三级缓存中删除 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}

的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化**。

注:该步骤参考博客:https://blog.csdn.net/u010853261/article/details/77940767

19,创建完成

创建完Bean之后将会调用getSingleton(String, ObjectFactory<?>)方法获取单实例Bean,并在该方法中最终调用addSingleton方法将该Bean添加到缓存(singletonObjects)中:

protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存中 this.singletonObjects.put(beanName, singletonObject); // 从二级缓存中删除 this.singletonFactories.remove(beanName); // 从三级缓存中删除 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}
你可能感兴趣的文章
.NET架构小技巧(5)——反射,架构人员法宝III
查看>>
C# 中的 in 参数和性能分析
查看>>
.NET架构小技巧(6)——什么是好的架构
查看>>
C#中形态各异的class
查看>>
.Net 5性能改进
查看>>
InfluxDB 2.0 之Flux语法篇
查看>>
TensorFlow 2学习和工业CV领域应用 心得分享
查看>>
程序员过关斩将--真的可以用版本号的方式来保证MQ消费消息的幂等性?
查看>>
Java面试必问JVM调优,那.NET5呢?
查看>>
把 Console 部署成 Windows 服务,四种方式总有一款适合你!
查看>>
缓存一致性和跨服务器查询的数据异构解决方案canal
查看>>
BeetleX之Websocket服务使用
查看>>
【源码】常用的人脸识别数据库以及上篇性别识别源码
查看>>
深入探究ASP.NET Core Startup的初始化
查看>>
跟我一起学Redis之Redis配置文件啃了一遍之后,从尴尬变得有底气了(总结了一张思维图)...
查看>>
一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试
查看>>
IdentityServer4系列 | 资源密码凭证模式
查看>>
TIOBE 11 月榜单:Python 挤掉 Java,Java的下跌趋势确立了?
查看>>
C#实现观察者模式
查看>>
使用Azure静态Web应用部署Blazor Webassembly应用
查看>>