本文共 37996 字,大约阅读时间需要 126 分钟。
我们首先找到测试的入口来一探究竟,相信下面这段代码大家都写过:
@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初始化过程。
通过分析ClassPathXmlApplicationContext的源代码可以知道,在创建ClassPathXmlApplicationContext容器时,构造方法首先做了以下两项工作
从上面代码的位置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)。
有了加载器,那么肯定就还需要加载器所加载的配置文件信息,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"});
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 配置资源进行载入。
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定义的调用。
我们从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)去加载配置资源。
从上步骤代码的位置1点进去,最终我们可以看到如下代码:
public int loadBeanDefinitions(String location, @Nullable SetactualResources) 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:调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源
其实就是 XmlBeanDefinitionReader 通过调用ClassPathXmlApplicationContext的父类DefaultResourceLoader的getResource()方法获取要加载的资源。
位置2:加载bean定义
在下个步骤中详细解释
总结一下这个流程的目的:寻找要加载资源的位置并加载。
继续上个环节的位置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看进去,最终会来到 DefaultDocumentLoader 的 loadDocument() 方法:
@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对象,并注册到容器中的。
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)。
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)。
接上步骤中的位置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); }}
可以看到,代码中根据标签类型,对标签内的元素进行解析。下面将以标签为例,一起来看一下是如何载入的。
从上步骤的位置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); // 新建集合存储别名,如果配置了别名就尝试获取 Listaliases = 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)。
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步的介绍,我们有了一个清晰的脉络:
通过这几步对配置资源的解析后,IOC容器大致完成了管理bean对象的准备工作,即初始化过程,但是最重要的依赖注入等操作还没有发生,现在在IOC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器中注册Bean的定义信息才能全部完成IOC容器的初始化过程。
从上面的流程中我们知道,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。
从上步骤中的位置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); ListupdatedDefinitions = 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容器控制反转的基础,正式有了这些注册的数据,容器才可以进行依赖注入。
描述请看代码
@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(); } }}
我们来一起看一下上步骤中的位置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); } ListbeanNames = 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调用doGetBeanprotectedT 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 MapsingletonObjects = new ConcurrentHashMap<>(256);// 二级缓存private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存private final Map > singletonFactories = new HashMap<>(16);
如果没有获取到则会将当前Bean已创建状态,然后执行创建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;}
这里只留下几个关键代码进行介绍:
上步骤中的位置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
创建完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
创建完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); }}