原文作者:Javadoop

原文地址:Spring IOC 容器源码分析

在继续往下之前,我们需要先了解 BeanDefinition。我们说 BeanFactory 是 Bean 容器,那么 Bean 又是什么呢?这里的 BeanDefinition 就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中。所以,如果有人问你 Bean 是什么的时候,你要知道 Bean 在代码层面上可以简单认为是 BeanDefinition 的实例。

BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。

BeanDefinition 接口定义

我们来看下 BeanDefinition 的接口定义:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {// 我们可以看到,默认只提供 sington 和 prototype 两种,// 很多读者可能知道还有 request, session, globalSession, application, websocket 这几种,// 不过,它们属于基于 web 的扩展。String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;// 比较不重要,直接跳过吧int ROLE_APPLICATION = 0;int ROLE_SUPPORT = 1;int ROLE_INFRASTRUCTURE = 2;// 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍// 一句话就是:继承父 Bean 的配置信息而已void setParentName(String parentName);// 获取父 BeanString getParentName();// 设置 Bean 的类名称,将来是要通过反射来生成实例的void setBeanClassName(String beanClassName);// 获取 Bean 的类名称String getBeanClassName();// 设置 bean 的 scopevoid setScope(String scope);String getScope();// 设置是否懒加载void setLazyInit(boolean lazyInit);boolean isLazyInit();// 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),// 是 depends-on="" 属性设置的值。void setDependsOn(String... dependsOn);// 返回该 Bean 的所有依赖String[] getDependsOn();// 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,// 如果根据名称注入,即使这边设置了 false,也是可以的void setAutowireCandidate(boolean autowireCandidate);// 该 Bean 是否可以注入到其他 Bean 中boolean isAutowireCandidate();// 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 beanvoid setPrimary(boolean primary);// 是否是 primary 的boolean isPrimary();// 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录// 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的void setFactoryBeanName(String factoryBeanName);// 获取工厂名称String getFactoryBeanName();// 指定工厂类中的 工厂方法名称void setFactoryMethodName(String factoryMethodName);// 获取工厂类中的 工厂方法名称String getFactoryMethodName();// 构造器参数ConstructorArgumentValues getConstructorArgumentValues();// Bean 中的属性值,后面给 bean 注入属性值的时候会说到MutablePropertyValues getPropertyValues();// 是否 singletonboolean isSingleton();// 是否 prototypeboolean isPrototype();// 如果这个 Bean 是被设置为 abstract,那么不能实例化,// 常用于作为 父bean 用于继承,其实也很少用......boolean isAbstract();int getRole();String getDescription();String getResourceDescription();BeanDefinition getOriginatingBeanDefinition();
}

这个 BeanDefinition 其实已经包含很多的信息了,暂时不清楚所有的方法对应什么东西没关系,希望看完本文后读者可以彻底搞清楚里面的所有东西。

这里接口虽然那么多,但是没有类似 getInstance() 这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例到哪里去了呢?别着急,这个要很后面才能讲到。

有了 BeanDefinition 的概念以后,我们再往下看 refreshBeanFactory() 方法中的剩余部分:

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);

虽然只有两个方法,但路还很长啊。。。customizeBeanFactory(beanFactory) 比较简单,就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {if (this.allowBeanDefinitionOverriding != null) {// 是否允许 Bean 定义覆盖beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.allowCircularReferences != null) {// 是否允许 Bean 间的循环依赖beanFactory.setAllowCircularReferences(this.allowCircularReferences);}
}

BeanDefinition 的覆盖问题可能会有开发者碰到这个坑,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。至于这两个属性怎么配置?我在附录中进行了介绍,尤其对于覆盖问题,很多人都希望禁止出现 Bean 覆盖,可是 Spring 默认是不同文件的时候可以覆盖的。之后的源码中还会出现这两个属性,读者有个印象就可以了,它们不是非常重要。

加载 Bean: loadBeanDefinitions

接下来是最重要的 loadBeanDefinitions(beanFactory) 方法了,这个方法将根据配置,加载各个 Bean,然后放到 BeanFactory 中。读取配置的操作在 XmlBeanDefinitionReader 中,其负责加载配置、解析。// AbstractXmlApplicationContext.java 80

/** 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReaderXmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的,// 我看了一下,没有类覆写这个方法,我们姑且当做不重要吧initBeanDefinitionReader(beanDefinitionReader);// 重点来了,继续往下loadBeanDefinitions(beanDefinitionReader);
}

现在还在这个类中,接下来用刚刚初始化的 Reader 开始来加载 xml 配置,这块代码读者可以选择性跳过,不是很重要。也就是说,下面这个代码块,读者可以很轻松地略过。// AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {// 往下看reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {// 2reader.loadBeanDefinitions(configLocations);}
}// 上面虽然有两个分支,不过第二个分支很快通过解析路径转换为 Resource 以后也会进到这里
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;// 注意这里是个 for 循环,也就是每个文件是一个 resourcefor (Resource resource : resources) {// 继续往下看counter += loadBeanDefinitions(resource);}// 最后返回 counter,表示总共加载了多少的 BeanDefinitionreturn counter;
}// XmlBeanDefinitionReader 303
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));
}// XmlBeanDefinitionReader 314
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}// 用一个 ThreadLocal 来存放配置文件资源Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 核心部分是这里,往下面看return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}
}// 还在这个文件中,第 388 行
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 这里就不看了,将 xml 文件转换为 Document 对象Document doc = doLoadDocument(inputSource, resource);// 继续return registerBeanDefinitions(doc, resource);}catch (...
}
// 还在这个文件中,第 505 行
// 返回值:返回从当前配置文件加载了多少数量的 Bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getRegistry().getBeanDefinitionCount();// 这里documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;
}
// DefaultBeanDefinitionDocumentReader 90
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();// 从 xml 根节点开始解析文件doRegisterBeanDefinitions(root);
}

经过漫长的链路,一个配置文件终于转换为一颗 DOM 树了,注意,这里指的是其中一个配置文件,不是所有的,读者可以看到上面有个 for 循环的。下面开始从根节点开始解析:

doRegisterBeanDefinitions:

// DefaultBeanDefinitionDocumentReader 116
protected void doRegisterBeanDefinitions(Element root) {// 我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义,// 这里为什么要定义一个 parent? 看到后面就知道了,是递归问题,// 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {// 这块说的是根节点 <beans ... profile="dev" /> 中的 profile 是否是当前环境需要的,// 如果当前环境配置的 profile 不包含此 profile,那就直接 return 了,不对此 <beans /> 解析// 不熟悉 profile 为何物,不熟悉怎么配置 profile 读者的请移步附录区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.isInfoEnabled()) {logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}preProcessXml(root); // 钩子// 往下看parseBeanDefinitions(root, this.delegate);postProcessXml(root); // 钩子this.delegate = parent;
}

preProcessXml(root) 和 postProcessXml(root) 是给子类用的钩子方法,鉴于没有被使用到,也不是我们的重点,我们直接跳过。这里涉及到了 profile 的问题,对于不了解的读者,我在附录中对 profile 做了简单的解释,读者可以参考一下。接下来,看核心解析方法 parseBeanDefinitions(root, this.delegate) :

// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />,
// 其他的属于 custom 的
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)) {// 解析 default namespace 下面的几个元素parseDefaultElement(ele, delegate);}else {// 解析其他 namespace 的元素delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}
}

从上面的代码,我们可以看到,对于每个配置来说,分别进入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 这两个分支了。parseDefaultElement(ele, delegate) 代表解析的节点是 <import /><alias /><bean /><beans /> 这几个。

这里的四个标签之所以是 default 的,是因为它们是处于这个 namespace 下定义的:

http://www.springframework.org/schema/beans

又到初学者科普时间,不熟悉 namespace 的读者请看下面贴出来的 xml,这里的第二行 xmlns 就是咯。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"default-autowire="byName">

而对于其他的标签,将进入到 delegate.parseCustomElement(element) 这个分支。如我们经常会使用到的 <mvc /><task /><context /><aop />等。

这些属于扩展,如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下所示。同时代码中需要提供相应的 parser 来解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。

假如读者想分析 <context:property-placeholder location="classpath:xx.properties" /> 的实现原理,就应该到 ContextNamespaceHandler 中找答案。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc   http://www.springframework.org/schema/mvc/spring-mvc.xsd  "default-autowire="byName">

同理,以后你要是碰到 <dubbo /> 这种标签,那么就应该搜一搜是不是有 DubboNamespaceHandler 这个处理类。

回过神来,看看处理 default 标签的方法:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {// 处理 <import /> 标签importBeanDefinitionResource(ele);}else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {// 处理 <alias /> 标签定义// <alias name="fromName" alias="toName"/>processAliasRegistration(ele);}else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {// 处理 <bean /> 标签定义,这也算是我们的重点吧processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// 如果碰到的是嵌套的 <beans /> 标签,需要递归doRegisterBeanDefinitions(ele);}
}

如果每个标签都说,那我不吐血,你们都要吐血了。我们挑我们的重点 <bean /> 标签出来说。

processBeanDefinition 解析 bean 标签

下面是 processBeanDefinition 解析 <bean /> 标签:

// DefaultBeanDefinitionDocumentReader 298

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中,细节往下看BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// 下面的几行先不要看,跳过先,跳过先,跳过先,后面会继续说的if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.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));}
}

继续往下看怎么解析之前,我们先看下 <bean /> 标签中可以定义哪些属性:

Property  
class 类的全限定名
name 可指定 id、name(用逗号、分号、空格分隔)
scope 作用域
constructor arguments 指定构造参数
properties 设置属性的值
autowiring mode no(默认值)、byName、byType、 constructor
lazy-initialization mode 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了)
initialization method bean 属性设置完成后,会调用这个方法
destruction method bean 销毁后的回调方法

上面表格中的内容我想大家都非常熟悉吧,如果不熟悉,那就是你不够了解 Spring 的配置了。简单地说就是像下面这样子:

<bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean"scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup"><!-- 可以用下面三种形式指定构造参数 --><constructor-arg type="int" value="7500000"/><constructor-arg name="years" value="7500000"/><constructor-arg index="0" value="7500000"/><!-- property 的几种情况 --><property name="beanOne"><ref bean="anotherExampleBean"/></property><property name="beanTwo" ref="yetAnotherBean"/><property name="integerProperty" value="1"/>
</bean>

当然,除了上面举例出来的这些,还有 factory-bean、factory-method、<lockup-method /><replaced-method /><meta /><qualifier /> 这几个,大家是不是熟悉呢?自己检验一下自己对 Spring 中 bean 的了解程度。有了以上这些知识以后,我们再继续往里看怎么解析 bean 元素,是怎么转换到 BeanDefinitionHolder 的。

// BeanDefinitionParserDelegate 428

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {return parseBeanDefinitionElement(ele, null);
}public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {String id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);List<String> aliases = new ArrayList<String>();// 将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个 别名列表数组,// 当然,如果你不定义 name 属性的话,就是空的了// 我在附录中简单介绍了一下 id 和 name 的配置,大家可以看一眼,有个20秒就可以了if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}String beanName = id;// 如果没有指定id, 那么用别名列表的第一个名字作为beanNameif (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}// 根据 <bean ...>...</bean> 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中,// 细节后面细说,先知道下面这行结束后,一个 BeanDefinition 实例就出来了。AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);// 到这里,整个 <bean /> 标签就算解析结束了,一个 BeanDefinition 就形成了。if (beanDefinition != null) {// 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生// 如果读者不感兴趣的话,我觉得不需要关心这块代码,对本文源码分析来说,这些东西不重要if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {// 按照我们的思路,这里 containingBean 是 null 的beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {// 如果我们不定义 id 和 name,那么我们引言里的那个例子://   1. beanName 为:com.javadoop.example.MessageServiceImpl#0//   2. beanClassName 为:com.javadoop.example.MessageServiceImplbeanName = this.readerContext.generateBeanName(beanDefinition);String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {// 把 beanClassName 设置为 Bean 的别名aliases.add(beanClassName);}}if (logger.isDebugEnabled()) {logger.debug("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}catch (Exception ex) {error(ex.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);// 返回 BeanDefinitionHolderreturn new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;
}

然后,我们再看看怎么根据配置创建 BeanDefinition 实例的:

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}try {String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}// 创建 BeanDefinition,然后设置类信息而已,很简单,就不贴代码了AbstractBeanDefinition bd = createBeanDefinition(className, parent);// 设置 BeanDefinition 的一堆属性,这些属性定义在 AbstractBeanDefinition 中parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));/*** 下面的一堆是解析 <bean>......</bean> 内部的子元素,* 解析出来以后的信息都放到 bd 的属性中*/// 解析 <meta />parseMetaElements(ele, bd);// 解析 <lookup-method />parseLookupOverrideSubElements(ele, bd.getMethodOverrides());// 解析 <replaced-method />parseReplacedMethodSubElements(ele, bd.getMethodOverrides());// 解析 <constructor-arg />parseConstructorArgElements(ele, bd);// 解析 <property />parsePropertyElements(ele, bd);// 解析 <qualifier />parseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;}catch (ClassNotFoundException ex) {error("Bean class [" + className + "] not found", ele, ex);}catch (NoClassDefFoundError err) {error("Class that bean class [" + className + "] depends on not found", ele, err);}catch (Throwable ex) {error("Unexpected failure during bean definition parsing", ele, ex);}finally {this.parseState.pop();}return null;
}

到这里,我们已经完成了根据 <bean /> 配置创建了一个 BeanDefinitionHolder 实例。注意,是一个。我们回到解析 <bean /> 的入口方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 将 <bean /> 节点转换为 BeanDefinitionHolder,就是上面说的一堆BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {// 如果有自定义属性的话,进行相应的解析,先忽略bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// 我们把这步叫做 注册Bean 吧BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// 注册完成后,发送事件,本文不展开说这个getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}
}

大家再仔细看一下这块吧,我们后面就不回来说这个了。这里已经根据一个 <bean /> 标签产生了一个 BeanDefinitionHolder 的实例,这个实例里面也就是一个 BeanDefinition 的实例和它的 beanName、aliases 这三个信息,注意,我们的关注点始终在 BeanDefinition 上:

public class BeanDefinitionHolder implements BeanMetadataElement {private final BeanDefinition beanDefinition;private final String beanName;private final String[] aliases;
...

然后我们准备注册这个 BeanDefinition,最后,把这个注册事件发送出去。下面,我们开始说说注册 Bean 吧。

注册 Bean

// BeanDefinitionReaderUtils 143

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();// 注册这个 Beanregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {// alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,// 获取的时候,会先将 alias 转换为 beanName,然后再查找registry.registerAlias(beanName, alias);}}
}

别名注册的放一边,毕竟它很简单,我们看看怎么注册 Bean。

// DefaultListableBeanFactory 793

@Override
public 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(...);}}// old? 还记得 “允许 bean 覆盖” 这个配置吗?allowBeanDefinitionOverridingBeanDefinition oldBeanDefinition;// 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中oldBeanDefinition = this.beanDefinitionMap.get(beanName);// 处理重复名称的 Bean 定义的情况if (oldBeanDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {// 如果不允许覆盖的话,抛异常throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...}else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// log...用框架定义的 Bean 覆盖用户自定义的 Bean }else if (!beanDefinition.equals(oldBeanDefinition)) {// log...用新的 Bean 覆盖旧的 Bean}else {// log...用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean}// 覆盖this.beanDefinitionMap.put(beanName, beanDefinition);}else {// 判断是否已经有其他的 Bean 开始初始化了.// 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面会有大篇幅说初始化过程,// 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beansif (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.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 {// 最正常的应该是进到这个分支。// 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);// 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字this.beanDefinitionNames.add(beanName);// 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,// 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的// 手动指的是通过调用以下方法注册的 bean ://     registerSingleton(String beanName, Object singletonObject)// 这不是重点,解释只是为了不让大家疑惑。Spring 会在后面"手动"注册一些 Bean,// 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的this.manualSingletonNames.remove(beanName);}// 这个不重要,在预初始化的时候会用到,不必管它。this.frozenBeanDefinitionNames = null;}if (oldBeanDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}
}

总结一下,到这里已经初始化了 Bean 容器,<bean /> 配置也相应的转换为了一个个 BeanDefinition,然后注册了各个 BeanDefinition 到注册中心,并且发送了注册事件。

Spring框架—SpringBean源码分析相关推荐

  1. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  2. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  3. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  4. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  5. Spring IOC 容器源码分析

    Spring IOC 容器源码分析 创建时间: 2017-11-15 00:00:00 [TOC] Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring ...

  6. 视频教程-RPC服务框架(Dubbo)源码分析-Java

    RPC服务框架(Dubbo)源码分析 鲁班学院-子路老师曾就职于谷歌.天猫电商等多家互联网公司,历任java架构师.研发经理等职位,参与并主导千万级并发电商网站与后端供应链研发体系搭建,多次参与电商大 ...

  7. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  8. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  9. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

最新文章

  1. PHP的转义函数 htmlspecialchars、strip_tags、addslashes解释
  2. Android_Servlet验证的简单登录程序
  3. python中关键字 表示空类型_Python小知识:如何在Python 3中转换数据类型
  4. 《Android游戏编程入门经典》——1.7节小结
  5. 第六次作业(C语言)
  6. 是什么时候开始学习gulp了
  7. python中的分支语句
  8. python从入门到精通需要多久-Python从入门到精通
  9. Roland SRX Series for Mac - 罗兰SRX系列音频插件合集
  10. 机器学习之K-means算法
  11. Java 多线程编程两个简单的例子
  12. 《HTML与CSS入门经典(第8版)》——第2章 发布Web内容2.1 创建用于本章的示例文件...
  13. oracle lpad nvl,Oracle的Lpad函数
  14. Datagrid的deleteRow多行移除问题
  15. 曲面积分(Surface Integral)
  16. VScode 完整安装、配置及完全卸载
  17. 第三届北京·顺义张镇灶王文化节即将开幕
  18. 物联网开发笔记(52)- 使用Micropython开发ESP32开发板之W5500以太网网络模块有线网络通信
  19. 企业应用系统集成的一般解决方案
  20. 炫龙笔记本的gtx965m显卡玩游戏很卡

热门文章

  1. Maven 进行多环境配置,使用profile文件进行配置
  2. VirtualBox下Ubuntu利用桥接方式上网
  3. 分享一个帮助用户全屏阅读的jQuery插件 - jQuery fullscreen
  4. Span元素的 width属性 无效果原因及解决方案
  5. Android中onInterceptTouchEvent、dispatchTouchEvent及onTouchEvent的调用顺序及内部原理
  6. owaspbwa tickets
  7. C++ Primer 7.33 练习编写成员函数
  8. mysql 查询优化 ~ explain与索引失效
  9. 本人开源项目 Lu-Rpc
  10. Error和Exception有什么区别?(还在总结)