内容简介:声明1.建议先阅读2.强烈建议阅读过程中要参照调用过程图,每篇都有对应的调用过程图
声明
1.建议先阅读 《Spring源码分析专题 —— 阅读指引》
2.强烈建议阅读过程中要参照调用过程图,每篇都有对应的调用过程图
3.写文不易,转载请标明出处
前言
在上文《Spring源码分析专题 —— IOC容器启动过程(上篇)》中我们已经寻找到 Spring IOC 启动过程的核心方法 refresh() ,本篇我们将详细讲解启动流程 「定位 -> 加载 -> 注册 -> 实例化」 中的加载与注册环节。
(定位的作用是获取到配置文件,通常我们spring的配置文件是 application.xml 或自定义的 spring-xxx.xml ,定位过程的细节不少,而对我们的主流程影响不大,所以关于定位过程将放在下篇补充讲解)
本篇继续使用上篇中的调用过程图☞ IOC容器启动调用过程图.jpg
加载与注册
我们再看一眼 refresh() 方法
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. /** * obtainFreshBeanFactory方法中会调用loadBeanDefinition方法,用于加载bean的定义 */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. /** 初始化所有非lazy-init的bean **/ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
在这个 refresh() 方法中,我们首先关注的是 obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { /** 实际是调用子类AbstractRefreshableApplicationContext中的refreshBeanFactory方法 **/ refreshBeanFactory(); return getBeanFactory(); }
obtainFreshBeanFactory() 方法中调用了 refreshBeanFactory() ,而这个方法是在 AbstractApplicationContext 的子类 AbstractRefreshableApplicationContext 实现
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); /** [note-by-leapmie] 调用子类XmlWebApplicationContext的loadBeanDefinitions方法 **/ loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
refreshBeanFactory方法中调用了 loadBeanDefinitions 方法,路线又回到了 XmlWebApplicationContext 容器,loadBeanDefinitions 方法是在 XmlWebApplicationContext 类中实现的
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); /** [note-by-leapmie] 注意传入的beanDefinitionReader是XmlBeanDefinitionReader **/ loadBeanDefinitions(beanDefinitionReader); }
方法最后是调用了重载的 loadBeanDefinitions 方法,传入的参数是 XmlBeanDefinitionReader 的对象,我们先看一看重载的 loadBeanDefinitions 方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { /** 实际是调用XmlBeanDefinitionReader的loadBeanDefinitions方法 **/ reader.loadBeanDefinitions(configLocation); } } }
可以看到,其实最后是调用 reader 的 loadBeanDefinitions 方法,此处 reader 的类型是 XmlBeanDefinitionReader ,所以我们查看 XmlBeanDefinitionReader 中的 loadBeanDefinitions 方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { . . . try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } /** do开头的方法表示是正真执行处理操作的方法 **/ 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(); } } }
此处我们关注的是 doLoadBeanDefinitions 方法,有个技巧,在Spring中,以do开头的方法都是最终实际执行逻辑处理的。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { /** [note-by-leapmie] 读取加载配置文件 **/ Document doc = doLoadDocument(inputSource, resource); /** [note-by-leapmie] 注册Bean **/ int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } . . . }
在这里有两个方法值得我们关注,一个是 doLoadDocument ,负责把配置文件读取为 Document 对象,这个方法中包含了「定位」过程的处理逻辑,关于定位过程我们在下篇再详细分析;第二个是 registerBeanDefinitions 方法,这个方法包含了「加载」和「注册」逻辑。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { /** [note-by-leapmie] 获取BeanDefinitionDocumentReader,此处获得的对象实际类型为DefaultBeanDefinitionDocumentReader **/ BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); /** [note-by-leapmie] 调用Reader的registerBeanDefinitions方法 */ documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
这里关键有两步,第一步是获取 documentReader ,第二步是调用 documentReader 的 registerBeanDefinitions 方法。稍微跟踪一下可知 documentReader 的实际类型是 DefaultBeanDefinitionDocumentReader,所以我们进入到 DefaultBeanDefinitionDocumentReader 的 registerBeanDefinitions 方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }
按照惯例,干活的是do开头的方法
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; 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); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); /** 解析转换为BeanDefinitions **/ parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
然后是 parseBeanDefinitions 方法
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; /** [note-by-leapmie] 判断元素是否属于默认的Namespace(当标签为<beans>时判断条件为真) **/ if (delegate.isDefaultNamespace(ele)) { /** [note-by-leapmie] 处理默认的Element **/ parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
isDefaultNamespaces 方法是判断元素是否属于默认的 Namespace ,通过跟踪可知,这个默认的 Namespace 是指 <beans>
标签, 我们知道在spring的配置文件中,对bean的定义是放在 <beans>
标签里边的,所以接下来的 parseDefaultElement 方法则是用于解析 bean 定义的。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { /** 处理<import>标签 **/ if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } /** 处理<alias>标签 **/ else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } /** 处理<bean>标签 **/ else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } /** 处理嵌套的<beans>标签 **/ else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
到这里我们就一目了然了,这很显然就是针对 <beans>
标签中的各种元素进行解析,对于其他标签我们不深究,直接看处理 <bean>
标签的 processBeanDefinition 方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { /** * [note-by-leapmie] * 调用BeanDefinitionParserDelegate的parseBeanDefinitionElement方法 * 返回一个包含BeanDefinition信息的BeanDefinitionHolder实例 * **/ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. /** * [note-by-leapmie] * 注册BeanDefinition * 传入的参数是刚刚获取到的BeanDefinitionHolder对象,再加上DefaultListableBeanFactory对象 * DefaultListableBeanFactory对象的由来需要追溯到AbstractRefreshableApplicationContext的refreshBeanFactory()方法中 * **/ 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)); } }
这里有两个非常关键的方法:
- 一个是 parseBeanDefinitionElement ,这个方法最终会返回一个持有 BeanDefinition 的 BeanDefinitionHolder 实例,我们在上篇开头的结论中已经说了,加载的过程其实就是把bean的定义转换成一个 BeanDefinition 对象,所以 parseBeanDefinitionElement 对应的便是 「加载」 过程;
- 另一个则是 registerBeanDefinition ,这个方法对应的便是「注册」过程。
接下来我们将分两部分分别讲解 parseBeanDefinitionElement 和 registerBeanDefinition 的内容
1. 加载 (parseBeanDefinitionElement)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
中的 delegate 是 BeanDefinitionParserDelegate 的实例,我们查看 BeanDefinitionParserDelegate 中的 decorateBeanDefinitionIfRequired 方法
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { . . . /** [note-by-leapmie] 此处调用的parseBeanDefinitionElement方法返回的是BeanDefinition **/ AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { . . . /** [note-by-leapmie] 把beanDefinition注入BeanDefinitionHolder中 **/ return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
这个方法先调用另一个重载的 parseBeanDefinitionElement 方法,然后把获得 beanDefinition 传入 BeanDefinitionHolder 。我们看一看重载的 parseBeanDefinitionElement 方法
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { . . . try { AbstractBeanDefinition bd = createBeanDefinition(className, parent); /** [note-by-leapmie] 解析bean定义的属性 **/ parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } . . . return null; }
这里的parse开头的方法都是对bean定义的属性标签进行解析,例如「name」、「singleton」、「lazy-init」等,大家可以自行深入了解每一个parse方法是如何解析各个属性的,在本文中就不再占用篇幅逐一讲解了。至此我们已经获取到了BeanDefinition的信息,下一步就到「注册」了。
2. 注册 (registerBeanDefinition)
所谓的注册,其实就是把BeanDefintion存储到IOC容器中,我们进入到 registerBeanDefinition 中看一看是如何实现的。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); /** [note-by-leapmie] 此处实际是调用DefaultListableBeanFactory的registerBeanDefinition方法 **/ registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
这里最终调用的是 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 其中 registry 是 DefaultListableBeanFactory 的实例。
(为什么是 DefaultListableBeanFactory ?感兴趣的可以去追溯一下,给出一点提示,在 AbstractRefreshableApplicationContext 的 refreshBeanFactory() 方法中会创建DefaultListableBeanFactory的实例,并在之后的所有关键方法中都会作为参数传入该实例,保证后续的调用流程中都能获取到该实例)
DefaultListableBeanFactory 的 registerBeanDefinition 方法如下
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { . . . if (existingDefinition != null) { . . . } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase /** * 把BeanDefinitionc添加到beanDefinitionMap中 * Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); * 由此可知,IOC的启动过程是先把Bean的定义解析转换为BeanDefiniton, * 最后存储于IOC容器(DefaultListableBeanFactory是一个IOC容器)的一个Map变量中。 * */ this.beanDefinitionMap.put(beanName, beanDefinition); /** * 把所有的Bean名存储于beanDefinitionNames列表中 */ this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
this.beanDefinitionMap.put(beanName, beanDefinition);
这一行是重点,字面意思已经很明显,就是把 beanName 和 beanDefinition 以 key-value 的形式存储于 beanDefinitionMap 中, beanDefinitionMap 的定义如下。
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
跟踪到这一步我们便可以得出结论:「 BeanDefinition 是存储在 DefaultListableBeanFactory 的一个 Map 数据结构中 」
以上所述就是小编给大家介绍的《Spring源码分析专题 —— IOC容器启动过程(中篇)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Java 多线程中篇
- (译)2019年前端性能优化清单 — 中篇
- EOS 主网上线半年大事纪(中篇)
- 超全面 MySQL 语句加锁分析(中篇)
- B端硬件产品从0到1(中篇):研发阶段
- 数据分析实战|人人都是产品经理网站(中篇):读者视角
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Chinese Authoritarianism in the Information Age
Routledge / 2018-2-13 / GBP 115.00
This book examines information and public opinion control by the authoritarian state in response to popular access to information and upgraded political communication channels among the citizens in co......一起来看看 《Chinese Authoritarianism in the Information Age》 这本书的介绍吧!