spring ioc 容器的加载流程

1.目标:熟练使用spring,并分析其源码,了解其中的思想。这篇主要介绍spring ioc 容器的加载

2.前提条件:会使用debug

3.源码分析方法:Intellj idea debug 模式下源码追溯

通过ClassPathXmlApplicationContext 进行xml 件的读取,从每个堆栈中读取程序的运行信息

4.注意:由于Spring的类继承体系比较复杂,不能全部贴图,所以只将分析源码之后发现的最主要的类继承结构类图贴在下方。

5.关于Spring Ioc

Demo:
我们从demo入手一步步进行代码追溯。

Spring Ioc Demo


1.定义数据访问接口IUserDao.java

  1. public interface IUserDao {
  2. public void InsertUser(String username,String password);
  3. }

2.定义IUserDao.java实现类IUserDaoImpl.java

  1. public class UserDaoImpl implements IUserDao {
  2. @Override
  3. public void InsertUser(String username, String password) {
  4. System.out.println("----UserDaoImpl --addUser----");
  5. }
  6. }

3.定义业务逻辑接口UserService.java

  1. public interface UserService {
  2. public void addUser(String username,String password);
  3. }

4.定义UserService.java实现类UserServiceImpl.java

  1. public class UserServiceImpl implements UserService {
  2. private IUserDao userDao; //set方法
  3. public void setUserDao(IUserDao userDao) {
  4. this.userDao = userDao;
  5. }
  6. @Override
  7. public void addUser(String username,String password) {
  8. userDao.InsertUser(username,password);
  9. }
  10. }

bean.xml配置文件

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
  5. <!--id名字自己取,class表示他代表的类,如果在包里的话需要加上包名-->
  6. <bean id="userService" class="UserServiceImpl" >
  7. <!--property代表是通过set方法注入,ref的值表示注入的内容-->
  8. <property name="userDao" ref="userDao"/>
  9. </bean>
  10. <bean id="userDao" class="UserDaoImpl"/>
  11. </beans>

ApplicationContext 继承结构


1.顶层接口:ApplicationContext

2.ClassPathXmlApplicationContext实现类继承AbstractXmlApplication 抽象类

3.AbstractXmlApplication 继承AbstractRefreshableConfigApplicationContext

4.AbstractRefreshableConfigApplicationContext抽象类继承AbstractRefreshableApplicationContext

5.AbstractRefreshableApplicationContext 继承 AbstractApplicationContext

6.AbstractApplicationContext 实现ConfigurableApplicationContext 接口

7.ConfigurableApplicationContext 接口继承

ApplicationContext接口

总体来说继承实现结构较深,内部使用了大量适配器模式。

以ClassPathXmlApplicationContext为例,继承类图如下图所示:

Spring Ioc容器加载过程源码详解


在开始之前,先介绍一个整体的概念。即spring ioc容器的加载,大体上经过以下几个过程:

资源文件定位、解析、注册、实例化

1.资源文件定位

其中资源文件定位,一般是在ApplicationContext的实现类里完成的,因为ApplicationContext接口继承ResourcePatternResolver 接口,ResourcePatternResolver接口继承ResourceLoader接口,ResourceLoader其中的getResource()方法,可以将外部的资源,读取为Resource类。


2.解析DefaultBeanDefinitionDocumentReader,

解析主要是在BeanDefinitionReader中完成的,最常用的实现类是XmlBeanDefinitionReader,其中的loadBeanDefinitions()方法,负责读取Resource,并完成后续的步骤。ApplicationContext完成资源文件定位之后,是将解析工作委托给XmlBeanDefinitionReader来完成的

解析这里涉及到很多步骤,最常见的情况,资源文件来自一个XML配置文件。首先是BeanDefinitionReader,将XML文件读取成w3c的Document文档。

DefaultBeanDefinitionDocumentReader对Document进行进一步解析。然后DefaultBeanDefinitionDocumentReader又委托给BeanDefinitionParserDelegate进行解析。如果是标准的xml namespace元素,会在Delegate内部完成解析,如果是非标准的xml namespace元素,则会委托合适的NamespaceHandler进行解析最终解析的结果都封装为BeanDefinitionHolder,至此解析就算完成。

后续会进行细致讲解。


3.注册

然后bean的注册是在BeanFactory里完成的,BeanFactory接口最常见的一个实现类是DefaultListableBeanFactory,它实现了BeanDefinitionRegistry接口,所以其中的registerBeanDefinition()方法,可以对BeanDefinition进行注册这里附带一提,最常见的XmlWebApplicationContext不是自己持有BeanDefinition的,它继承自AbstractRefreshableApplicationContext,其持有一个DefaultListableBeanFactory的字段,就是用它来保存BeanDefinition

所谓的注册,其实就是将BeanDefinition的name和实例,保存到一个Map中。刚才说到,最常用的实现DefaultListableBeanFactory,其中的字段就是beanDefinitionMap,是一个ConcurrentHashMap。

代码如下:

>1.DefaultListableBeanFactory继承实现关系

  1. public class DefaultListableBeanFactory
  2. extends
  3. AbstractAutowireCapableBeanFactory
  4. implements
  5. ConfigurableListableBeanFactory,
  6. BeanDefinitionRegistry,
  7. Serializable {
  8. // DefaultListableBeanFactory的实例中最终保存了所有注册的bean beanDefinitionMap
  9. /** Map of bean definition objects, keyed by bean name */
  10. private final Map<String, BeanDefinition> beanDefinitionMap
  11. = new ConcurrentHashMap<String, BeanDefinition>(64);
  12. //实现BeanDefinitionRegistry中定义的registerBeanDefinition()抽象方法
  13. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
  14. }

>2.BeanDefinitionRegistry接口

  1. public interface BeanDefinitionRegistry extends AliasRegistry {
  2. //定义注册BeanDefinition实例的抽象方法
  3. void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;

4.实例化


注册也完成之后,在BeanFactory的getBean()方法之中,会完成初始化,也就是依赖注入的过程

大体上的流程就是这样。

refresh()方法

1.目标:

这篇记录debug 追溯源码的过程,大概分三个篇幅,这是第一篇,现整体了解一下运行流程,定位资源加载,资源解析,bean 注册发生的位置。

2.记录结构:

1.调试栈截图

2.整体流程

3.bean.xml的处理

每段代码下面有相应的讲解

调试栈截图


每个栈帧中方法的行号都有标明,按照行号追溯源码,然后配合教程能够快速学习。

整体流程


ioc容器实例化代码

  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

进入代码中一步步追溯,发现重要方法:refresh();

如下所示:

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. // Prepare this context for refreshing.
  4. prepareRefresh();
  5. //beanFactory实例化方法 单步调试入口
  6. // Tell the subclass to refresh the internal bean factory.
  7. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  8. // Prepare the bean factory for use in this context.
  9. prepareBeanFactory(beanFactory);
  10. try {
  11. // Allows post-processing of the bean factory in context subclasses.
  12. postProcessBeanFactory(beanFactory);
  13. // Invoke factory processors registered as beans in the context.
  14. invokeBeanFactoryPostProcessors(beanFactory);
  15. // Register bean processors that intercept bean creation.
  16. registerBeanPostProcessors(beanFactory);
  17. // Initialize message source for this context.
  18. initMessageSource();
  19. // Initialize event multicaster for this context.
  20. initApplicationEventMulticaster();
  21. // Initialize other special beans in specific context subclasses.
  22. onRefresh();
  23. // Check for listener beans and register them.
  24. registerListeners();
  25. // Instantiate all remaining (non-lazy-init) singletons.
  26. finishBeanFactoryInitialization(beanFactory);
  27. // Last step: publish corresponding event.
  28. finishRefresh();
  29. }
  30. catch (BeansException ex) {
  31. // Destroy already created singletons to avoid dangling resources.
  32. destroyBeans();
  33. // Reset 'active' flag.
  34. cancelRefresh(ex);
  35. // Propagate exception to caller.
  36. throw ex;
  37. }
  38. }
  39. }

首先这个方法是同步的,以避免重复刷新。然后刷新的每个步骤,都放在单独的方法里,比较清晰,可以按顺序一个个看

首先是prepareRefresh()方法

  1. protected void prepareRefresh() {
  2. this.startupDate = System.currentTimeMillis();
  3. synchronized (this.activeMonitor) {
  4. this.active = true;
  5. }
  6. if (logger.isInfoEnabled()) {
  7. logger.info("Refreshing " + this);
  8. }
  9. // Initialize any placeholder property sources in the context environment
  10. initPropertySources();
  11. // Validate that all properties marked as required are resolvable
  12. // see ConfigurablePropertyResolver#setRequiredProperties
  13. this.environment.validateRequiredProperties();
  14. }

这个方法里做的事情不多,记录了开始时间,输出日志,另外initPropertySources()方法和validateRequiredProperties()方法一般都没有做什么事。

然后是核心的obtainFreshBeanFactory()方法,这个方法是初始化BeanFactory,是整个refresh()方法的核心,其中完成了配置文件的加载、解析、注册,后面会专门详细说 。

这里要说明一下,ApplicationContext实现了BeanFactory接口,并实现了ResourceLoader、MessageSource等接口,可以认为是增强的BeanFactory。但是ApplicationContext并不自己重复实现BeanFactory定义的方法,而是委托给DefaultListableBeanFactory来实现。这种设计思路也是值得学习的。

后面的 prepareBeanFactory()、postProcessBeanFactory()、invokeBeanFactoryPostProcessors()、registerBeanPostProcessors()、initMessageSource()、initApplicationEventMulticaster()、onRefresh()、registerListeners()、finishBeanFactoryInitialization()、finishRefresh()等方法,是添加一些后处理器、广播、拦截器等,就不一个个细说了

其中的关键方法是finishBeanFactoryInitialization(),在这个方法中,会对刚才注册的Bean(不延迟加载的),进行实例化,所以也是一个核心方法。

bean.xml的处理


从整体上介绍完了流程,接下来就重点看obtainFreshBeanFactory()方法,上文说到,在这个方法里,完成了配置文件的加载、解析、注册

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  2. refreshBeanFactory();
  3. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  6. }
  7. return beanFactory;
  8. }

这个方法做了2件事,首先通过refreshBeanFactory()方法,创建了DefaultListableBeanFactory的实例,并进行初始化。

  1. protected final void refreshBeanFactory() throws BeansException {
  2. if (hasBeanFactory()) {
  3. destroyBeans();
  4. closeBeanFactory();
  5. }
  6. try {
  7. DefaultListableBeanFactory beanFactory = createBeanFactory();
  8. beanFactory.setSerializationId(getId());
  9. customizeBeanFactory(beanFactory);
  10. loadBeanDefinitions(beanFactory);
  11. synchronized (this.beanFactoryMonitor) {
  12. this.beanFactory = beanFactory;
  13. }
  14. }
  15. catch (IOException ex) {
  16. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  17. }
  18. }

首先如果已经有BeanFactory实例,就先清空。然后通过createBeanFactory()方法,创建一个DefaultListableBeanFactory的实例

  1. protected DefaultListableBeanFactory createBeanFactory() {
  2. return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  3. }

接下来设置ID唯一标识

  1. beanFactory.setSerializationId(getId());

然后允许用户进行一些自定义的配置

  1. protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
  2. if (this.allowBeanDefinitionOverriding != null) {
  3. beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  4. }
  5. if (this.allowCircularReferences != null) {
  6. beanFactory.setAllowCircularReferences(this.allowCircularReferences);
  7. }
  8. beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
  9. }

最后,就是核心的loadBeanDefinitions()方法

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  2. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  3. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  4. // Configure the bean definition reader with this context's
  5. // resource loading environment.
  6. beanDefinitionReader.setEnvironment(this.getEnvironment());
  7. beanDefinitionReader.setResourceLoader(this);
  8. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  9. // Allow a subclass to provide custom initialization of the reader,
  10. // then proceed with actually loading the bean definitions.
  11. initBeanDefinitionReader(beanDefinitionReader);
  12. loadBeanDefinitions(beanDefinitionReader);
  13. }

这里首先会创建一个XmlBeanDefinitionReader的实例,然后进行初始化。这个XmlBeanDefinitionReader中其实传递的BeanDefinitionRegistry类型的实例,为什么可以传递一个beanFactory呢,因为DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这里是多态的使用。

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  2. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  3. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  4. // Configure the bean definition reader with this context's
  5. // resource loading environment.
  6. beanDefinitionReader.setEnvironment(this.getEnvironment());
  7. beanDefinitionReader.setResourceLoader(this);
  8. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  9. // Allow a subclass to provide custom initialization of the reader,
  10. // then proceed with actually loading the bean definitions.
  11. initBeanDefinitionReader(beanDefinitionReader);
  12. }

这里要说明一下,ApplicationContext并不自己负责配置文件的加载、解析、注册,而是将这些工作委托给XmlBeanDefinitionReader来做。

  1. loadBeanDefinitions(beanDefinitionReader);

这行代码,就是Bean定义读取实际发生的地方。这里的工作,主要是XmlBeanDefinitionReader来完成的,下一篇博客会详细介绍这个过程。

loadBeanDefinitions

loadBeanDefinitions: 源码阅读


入口是loadBeanDefinitions方法

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)
  2. throws IOException {
  3. String[] configLocations = getConfigLocations();
  4. if (configLocations != null) {
  5. for (String configLocation : configLocations) {
  6. reader.loadBeanDefinitions(configLocation);
  7. }
  8. }
  9. }

这是解析过程最外围的代码,首先要获取到配置文件的路径,这在之前已经完成了。

然后将每个配置文件的路径,作为参数传给BeanDefinitionReader的loadBeanDefinitions方法里

  1. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
  2. return loadBeanDefinitions(location, null);
  3. }

这个方法又调用了重载方法

  1. public int loadBeanDefinitions(String location, Set<Resource> actualResources)
  2. throws BeanDefinitionStoreException {
  3. ResourceLoader resourceLoader = getResourceLoader();
  4. if (resourceLoader == null) {
  5. throw new BeanDefinitionStoreException(
  6. "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
  7. }
  8. if (resourceLoader instanceof ResourcePatternResolver) {
  9. // Resource pattern matching available.
  10. try {
  11. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  12. int loadCount = loadBeanDefinitions(resources);
  13. if (actualResources != null) {
  14. for (Resource resource : resources) {
  15. actualResources.add(resource);
  16. }
  17. }
  18. if (logger.isDebugEnabled()) {
  19. logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
  20. }
  21. return loadCount;
  22. }
  23. catch (IOException ex) {
  24. throw new BeanDefinitionStoreException(
  25. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  26. }
  27. }
  28. else {
  29. // Can only load single resources by absolute URL.
  30. Resource resource = resourceLoader.getResource(location);
  31. int loadCount = loadBeanDefinitions(resource);
  32. if (actualResources != null) {
  33. actualResources.add(resource);
  34. }
  35. if (logger.isDebugEnabled()) {
  36. logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
  37. }
  38. return loadCount;
  39. }
  40. }

首先getResourceLoader()的实现的前提条件是因为XmlBeanDefinitionReader在实例化的时候已经确定了创建了实例ResourceLoader实例, 代码位于 AbstractBeanDefinitionReader

  1. protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
  2. Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
  3. this.registry = registry;
  4. // Determine ResourceLoader to use.
  5. if (this.registry instanceof ResourceLoader) {
  6. this.resourceLoader = (ResourceLoader) this.registry;
  7. } else {
  8. this.resourceLoader = new PathMatchingResourcePatternResolver();
  9. }
  10. // Inherit Environment if possible
  11. if (this.registry instanceof EnvironmentCapable) {
  12. this.environment = ((EnvironmentCapable)this.registry).getEnvironment();
  13. } else {
  14. this.environment = new StandardEnvironment();
  15. }
  16. }

这个方法比较长,BeanDefinitionReader不能直接加载配置文件,需要把配置文件封装成Resource,然后才能调用重载方法loadBeanDefinitions()。所以这个方法其实就是2段,第一部分是委托ResourceLoader将配置文件封装成Resource,第二部分是调用loadBeanDefinitions(),对Resource进行解析

而这里的ResourceLoader,就是前面的XmlWebApplicationContext,因为ApplicationContext接口,是继承自ResourceLoader接口的

Resource也是一个接口体系,在web环境下,这里就是ServletContextResource

接下来进入重载方法loadBeanDefinitions()

  1. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  2. Assert.notNull(resources, "Resource array must not be null");
  3. int counter = 0;
  4. for (Resource resource : resources) {
  5. counter += loadBeanDefinitions(resource);
  6. }
  7. return counter;
  8. }

这里就不用说了,就是把每一个Resource作为参数,继续调用重载方法。读spring源码,会发现重载方法特别多。

  1. public int loadBeanDefinitions(Resource resource) throws
  2. BeanDefinitionStoreException {
  3. return loadBeanDefinitions(new EncodedResource(resource));
  4. }

还是重载方法,不过这里对传进来的Resource又进行了一次封装,变成了编码后的Resource。

  1. public int loadBeanDefinitions(EncodedResource encodedResource)
  2. throws BeanDefinitionStoreException {
  3. Assert.notNull(encodedResource, "EncodedResource must not be null");
  4. if (logger.isInfoEnabled()) {
  5. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  6. }
  7. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  8. if (currentResources == null) {
  9. currentResources = new HashSet<EncodedResource>(4);
  10. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  11. }
  12. if (!currentResources.add(encodedResource)) {
  13. throw new BeanDefinitionStoreException(
  14. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  15. }
  16. try {
  17. InputStream inputStream = encodedResource.getResource().getInputStream();
  18. try {
  19. InputSource inputSource = new InputSource(inputStream);
  20. if (encodedResource.getEncoding() != null) {
  21. inputSource.setEncoding(encodedResource.getEncoding());
  22. }
  23. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  24. }
  25. finally {
  26. inputStream.close();
  27. }
  28. }
  29. catch (IOException ex) {
  30. throw new BeanDefinitionStoreException(
  31. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  32. }
  33. finally {
  34. currentResources.remove(encodedResource);
  35. if (currentResources.isEmpty()) {
  36. this.resourcesCurrentlyBeingLoaded.remove();
  37. }
  38. }
  39. }

这个就是loadBeanDefinitions()的最后一个重载方法,比较长,可以拆看来看。

  1. Assert.notNull(encodedResource, "EncodedResource must not be null");
  2. if (logger.isInfoEnabled()) {
  3. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  4. }
  5. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  6. if (currentResources == null) {
  7. currentResources = new HashSet<EncodedResource>(4);
  8. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  9. }
  10. if (!currentResources.add(encodedResource)) {
  11. throw new BeanDefinitionStoreException(
  12. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  13. }

这第一部分,是处理线程相关的工作,把当前正在解析的Resource,设置为当前Resource。

  1. try {
  2. InputStream inputStream = encodedResource.getResource().getInputStream();
  3. try {
  4. InputSource inputSource = new InputSource(inputStream);
  5. if (encodedResource.getEncoding() != null) {
  6. inputSource.setEncoding(encodedResource.getEncoding());
  7. }
  8. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  9. }
  10. finally {
  11. inputStream.close();
  12. }
  13. }

这里是第二部分,是核心,首先把Resource还原为InputStream,然后调用实际解析的方法doLoadBeanDefinitions()。可以看到,这种命名方式是很值得学习的,一种业务方法,比如parse(),可能需要做一些外围的工作,然后实际解析的方法,可以命名为doParse()。这种doXXX()的命名方法,在很多开源框架中都有应用,比如logback等。

接下来就看一下这个doLoadBeanDefinitions()方法

  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2. throws BeanDefinitionStoreException {
  3. try {
  4. Document doc = doLoadDocument(inputSource, resource);return registerBeanDefinitions(doc, resource);
  5. return registerBeanDefinitions(doc, resource);
  6. }
  7. catch (BeanDefinitionStoreException ex) {
  8. throw ex;
  9. }
  10. catch (SAXParseException ex) {
  11. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  12. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  13. }
  14. catch (SAXException ex) {
  15. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  16. "XML document from " + resource + " is invalid", ex);
  17. }
  18. catch (ParserConfigurationException ex) {
  19. throw new BeanDefinitionStoreException(resource.getDescription(),
  20. "Parser configuration exception parsing XML from " + resource, ex);
  21. }
  22. catch (IOException ex) {
  23. throw new BeanDefinitionStoreException(resource.getDescription(),
  24. "IOException parsing XML document from " + resource, ex);
  25. }
  26. catch (Throwable ex) {
  27. throw new BeanDefinitionStoreException(resource.getDescription(),
  28. "Unexpected exception parsing XML document from " + resource, ex);
  29. }
  30. }

抛开异常处理:核心代码如下:

  1. Document doc = doLoadDocument(inputSource, resource);
  2. return registerBeanDefinitions(doc, resource);

doLoadDocument方法将InputStream读取成标准的Document对象,然后调用registerBeanDefinitions(),进行解析工作。

  1. protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
  2. return this.documentLoader.loadDocument(inputSource,
  3. getEntityResolver(), this.errorHandler,
  4. getValidationModeForResource(resource),
  5. isNamespaceAware());
  6. }

接下来就看一下这个核心方法registerBeanDefinitions

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. //创建的其实是DefaultBeanDefinitionDocumentReader 的实例,利用反射创建的。
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. documentReader.setEnvironment(this.getEnvironment());
  5. int countBefore = getRegistry().getBeanDefinitionCount();
  6. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  7. return getRegistry().getBeanDefinitionCount() - countBefore;
  8. }

这里注意两点 :

1.Document对象

首先这个Document对象,是W3C定义的标准XML对象,跟spring无关。其次这个registerBeanDefinitions方法,我觉得命名有点误导性。因为这个时候实际上解析还没有开始,怎么直接就注册了呢。比较好的命名,我觉得可以是parseAndRegisterBeanDefinitions()。

2.documentReader的创建时使用反射创建的,代码如下

  1. protected BeanDefinitionDocumentReader
  2. createBeanDefinitionDocumentReader() {
  3. return BeanDefinitionDocumentReader.class.cast(BeanUtils.
  4. instantiateClass(this.documentReaderClass));
  5. }

instantiateClass方法中传入了一个Class类型的参数。追溯发现下述代码:

  1. private Class<?> documentReaderClass =
  2. DefaultBeanDefinitionDocumentReader.class;

所以创建的documentReaderClass是DefaultBeanDefinitionDocumentReader类的实例。

接下来就进入BeanDefinitionDocumentReader 中定义的registerBeanDefinitions()方法看看

  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2. this.readerContext = readerContext;
  3. logger.debug("Loading bean definitions");
  4. Element root = doc.getDocumentElement();
  5. doRegisterBeanDefinitions(root);
  6. }

处理完外围事务之后,进入doRegisterBeanDefinitions()方法,这种命名规范,上文已经介绍过了

  1. protected void doRegisterBeanDefinitions(Element root) {
  2. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  3. if (StringUtils.hasText(profileSpec)) {
  4. Assert.state(this.environment != null, "environment property must not be null");
  5. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  6. if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  7. return;
  8. }
  9. }
  10. // any nested <beans> elements will cause recursion in this method. In
  11. // order to propagate and preserve <beans> default-* attributes correctly,
  12. // keep track of the current (parent) delegate, which may be null. Create
  13. // the new (child) delegate with a reference to the parent for fallback purposes,
  14. // then ultimately reset this.delegate back to its original (parent) reference.
  15. // this behavior emulates a stack of delegates without actually necessitating one.
  16. BeanDefinitionParserDelegate parent = this.delegate;
  17. this.delegate = createHelper(readerContext, root, parent);
  18. preProcessXml(root);
  19. parseBeanDefinitions(root, this.delegate);
  20. postProcessXml(root);
  21. this.delegate = parent;
  22. }

这个方法也比较长,拆开来看

  1. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  2. if (StringUtils.hasText(profileSpec)) {
  3. Assert.state(this.environment != null, "environment property must not be null");
  4. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  5. if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  6. return;
  7. }
  8. }

如果配置文件中元素,配有profile属性,就会进入这一段,不过一般都是不会的

  1. BeanDefinitionParserDelegate parent = this.delegate;
  2. this.delegate = createHelper(readerContext, root, parent);
  3. preProcessXml(root);
  4. parseBeanDefinitions(root, this.delegate);
  5. postProcessXml(root);
  6. this.delegate = parent;

然后这里创建了BeanDefinitionParserDelegate对象,preProcessXml()和postProcessXml()都是空方法,核心就是parseBeanDefinitions()方法。这里又把BeanDefinition解析和注册的工作,委托给了BeanDefinitionParserDelegate对象,在parseBeanDefinitions()方法中完成

总的来说,解析工作的委托链是这样的:ClassPathXmlApplicationContext,XmlBeanDefinitionReader,DefaultBeanDefinitionDocumentReader,BeanDefinitionParserDelegate

ClassPathXmlApplicationContext作为最外围的组件,发起解析的请求

XmlBeanDefinitionReader将配置文件路径封装为Resource,读取出w3c定义的Document对象,然后委托给DefaultBeanDefinitionDocumentReader

DefaultBeanDefinitionDocumentReader就开始做实际的解析工作了,但是涉及到bean的具体解析,它还是会继续委托给BeanDefinitionParserDelegate来做。

接下来在parseBeanDefinitions()方法中发生了什么,以及BeanDefinitionParserDelegate类完成的工作,在下一篇博客中继续介绍。

loadBeanDefinitions

BeanDefinition的解析,已经走到了DefaultBeanDefinitionDocumentR

eader里,这时候配置文件已经被加载,并解析成w3c的Document对象。这篇博客就接着介绍,DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate类,是怎么协同完成bean的解析和注册的。

  1. BeanDefinitionParserDelegate parent = this.delegate;
  2. this.delegate = createHelper(readerContext, root, parent);
  3. preProcessXml(root);
  4. parseBeanDefinitions(root, this.delegate);
  5. postProcessXml(root);
  6. this.delegate = parent;

这段代码,创建了一个BeanDefinitionParserDelegate组件,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法

其中preProcessXml()和postProcessXml()默认是空方法,接下来就看下parseBeanDefinitions()方法

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (delegate.isDefaultNamespace(ele)) {
  9. parseDefaultElement(ele, delegate);
  10. }
  11. else {
  12. delegate.parseCustomElement(ele);
  13. }
  14. }
  15. }
  16. }
  17. else {
  18. delegate.parseCustomElement(root);
  19. }
  20. }

从这个方法开始,BeanDefinitionParserDelegate就开始发挥作用了,判断当前解析元素是否属于默认的命名空间,如果是的话,就调用parseDefaultElement()方法,否则调用delegate上parseCustomElement()方法

  1. public boolean isDefaultNamespace(String namespaceUri) {
  2. return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
  3. }
  4. public boolean isDefaultNamespace(Node node) {
  5. return isDefaultNamespace(getNamespaceURI(node));
  6. }

只有http://www.springframework.org/schema/beans,会被认为是默认的命名空间。也就是说,beans、bean这些元素,会认为属于默认的命名空间,而像task:scheduled这些,就认为不属于默认命名空间。

根节点beans的一个子节点bean,是属于默认命名空间的,所以会进入parseDefaultElement()方法

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  3. importBeanDefinitionResource(ele);
  4. }
  5. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  6. processAliasRegistration(ele);
  7. }
  8. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  9. processBeanDefinition(ele, delegate);
  10. }
  11. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  12. // recurse
  13. doRegisterBeanDefinitions(ele);
  14. }
  15. }

这里可能会有4种情况,import、alias、bean、beans,分别有一个方法与之对应,这里解析的是bean元素,所以会进入processBeanDefinition()方法

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  3. if (bdHolder != null) {
  4. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  5. try {
  6. // Register the final decorated instance.
  7. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  8. }
  9. catch (BeanDefinitionStoreException ex) {
  10. getReaderContext().error("Failed to register bean definition with name '" +
  11. bdHolder.getBeanName() + "'", ele, ex);
  12. }
  13. // Send registration event.
  14. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  15. }
  16. }

这里主要有3个步骤,先是委托delegate对bean进行解析,然后委托delegate对bean进行装饰,最后由一个工具类来完成BeanDefinition的注册

可以看出来,DefaultBeanDefinitionDocumentReader不负责任何具体的bean解析,它面向的是xml Document对象,根据其元素的命名空间和名称,起一个类似路由的作用(不过,命名空间的判断,也是委托给delegate来做的)。所以这个类的命名,是比较贴切的,突出了其面向Document的特性。具体的工作,是由BeanDefinitionParserDelegate来完成的

下面就看下parseBeanDefinitionElement()方法

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  2. String id = ele.getAttribute(ID_ATTRIBUTE);
  3. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  4. List<String> aliases = new ArrayList<String>();
  5. if (StringUtils.hasLength(nameAttr)) {
  6. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  7. aliases.addAll(Arrays.asList(nameArr));
  8. }
  9. String beanName = id;
  10. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  11. beanName = aliases.remove(0);
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("No XML 'id' specified - using '" + beanName +
  14. "' as bean name and " + aliases + " as aliases");
  15. }
  16. }
  17. if (containingBean == null) {
  18. checkNameUniqueness(beanName, aliases, ele);
  19. }
  20. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  21. if (beanDefinition != null) {
  22. if (!StringUtils.hasText(beanName)) {
  23. try {
  24. if (containingBean != null) {
  25. beanName = BeanDefinitionReaderUtils.generateBeanName(
  26. beanDefinition, this.readerContext.getRegistry(), true);
  27. }
  28. else {
  29. beanName = this.readerContext.generateBeanName(beanDefinition);
  30. // Register an alias for the plain bean class name, if still possible,
  31. // if the generator returned the class name plus a suffix.
  32. // This is expected for Spring 1.2/2.0 backwards compatibility.
  33. String beanClassName = beanDefinition.getBeanClassName();
  34. if (beanClassName != null &&
  35. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  36. aliases.add(beanClassName);
  37. }
  38. }
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Neither XML 'id' nor 'name' specified - " +
  41. "using generated bean name [" + beanName + "]");
  42. }
  43. }
  44. catch (Exception ex) {
  45. error(ex.getMessage(), ele);
  46. return null;
  47. }
  48. }
  49. String[] aliasesArray = StringUtils.toStringArray(aliases);
  50. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  51. }
  52. return null;
  53. }

这个方法很长,可以分成三段来看

  1. String id = ele.getAttribute(ID_ATTRIBUTE);
  2. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  3. List<String> aliases = new ArrayList<String>();
  4. if (StringUtils.hasLength(nameAttr)) {
  5. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  6. aliases.addAll(Arrays.asList(nameArr));
  7. }
  8. String beanName = id;
  9. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  10. beanName = aliases.remove(0);
  11. if (logger.isDebugEnabled()) {
  12. logger.debug("No XML 'id' specified - using '" + beanName +
  13. "' as bean name and " + aliases + " as aliases");
  14. }
  15. }
  16. if (containingBean == null) {
  17. checkNameUniqueness(beanName, aliases, ele);
  18. }

这一段,主要是处理一些跟alias,id等标识相关的东西

  1. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

这一行是核心,进行实际的解析

  1. if (beanDefinition != null) {
  2. if (!StringUtils.hasText(beanName)) {
  3. try {
  4. if (containingBean != null) {
  5. beanName = BeanDefinitionReaderUtils.generateBeanName(
  6. beanDefinition, this.readerContext.getRegistry(), true);
  7. }
  8. else {
  9. beanName = this.readerContext.generateBeanName(beanDefinition);
  10. // Register an alias for the plain bean class name, if still possible,
  11. // if the generator returned the class name plus a suffix.
  12. // This is expected for Spring 1.2/2.0 backwards compatibility.
  13. String beanClassName = beanDefinition.getBeanClassName();
  14. if (beanClassName != null &&
  15. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  16. !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  17. aliases.add(beanClassName);
  18. }
  19. }
  20. if (logger.isDebugEnabled()) {
  21. logger.debug("Neither XML 'id' nor 'name' specified - " +
  22. "using generated bean name [" + beanName + "]");
  23. }
  24. }
  25. catch (Exception ex) {
  26. error(ex.getMessage(), ele);
  27. return null;
  28. }
  29. }
  30. String[] aliasesArray = StringUtils.toStringArray(aliases);
  31. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  32. }

这段是后置处理,对beanName进行处理

前置处理和后置处理,不是核心,就不细看了,重点看下核心的那一行调用

  1. public AbstractBeanDefinition parseBeanDefinitionElement(
  2. Element ele, String beanName, BeanDefinition containingBean) {
  3. this.parseState.push(new BeanEntry(beanName));
  4. String className = null;
  5. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  6. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  7. }
  8. try {
  9. String parent = null;
  10. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  11. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  12. }
  13. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  14. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  15. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  16. parseMetaElements(ele, bd);
  17. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  18. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  19. parseConstructorArgElements(ele, bd);
  20. parsePropertyElements(ele, bd);
  21. parseQualifierElements(ele, bd);
  22. bd.setResource(this.readerContext.getResource());
  23. bd.setSource(extractSource(ele));
  24. return bd;
  25. }
  26. catch (ClassNotFoundException ex) {
  27. error("Bean class [" + className + "] not found", ele, ex);
  28. }
  29. catch (NoClassDefFoundError err) {
  30. error("Class that bean class [" + className + "] depends on not found", ele, err);
  31. }
  32. catch (Throwable ex) {
  33. error("Unexpected failure during bean definition parsing", ele, ex);
  34. }
  35. finally {
  36. this.parseState.pop();
  37. }
  38. return null;
  39. }

这个方法也挺长的,拆开看看

  1. this.parseState.push(new BeanEntry(beanName));
  2. String className = null;
  3. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  4. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  5. }

这段是从配置中抽取出类名。接下来的长长一段,把异常处理先抛开,看看实际的业务

  1. String parent = null;
  2. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  3. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  4. }
  5. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  6. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  7. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  8. parseMetaElements(ele, bd);
  9. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  10. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  11. parseConstructorArgElements(ele, bd);
  12. parsePropertyElements(ele, bd);
  13. parseQualifierElements(ele, bd);
  14. bd.setResource(this.readerContext.getResource());
  15. bd.setSource(extractSource(ele));
  16. return bd;

这里每个方法的命名,就说明了是要干什么,可以一个个跟进去看,本文就不细说了。总之,经过这里的解析,就得到了一个完整的BeanDefinitionHolder。只是说明一下,如果在配置文件里,没有对一些属性进行设置,比如autowire-candidate等,那么这个解析生成的BeanDefinition,都会得到一个默认值

然后,对这个Bean做一些必要的装饰

  1. public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
  2. Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
  3. BeanDefinitionHolder finalDefinition = definitionHolder;
  4. // Decorate based on custom attributes first.
  5. NamedNodeMap attributes = ele.getAttributes();
  6. for (int i = 0; i < attributes.getLength(); i++) {
  7. Node node = attributes.item(i);
  8. finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
  9. }
  10. // Decorate based on custom nested elements.
  11. NodeList children = ele.getChildNodes();
  12. for (int i = 0; i < children.getLength(); i++) {
  13. Node node = children.item(i);
  14. if (node.getNodeType() == Node.ELEMENT_NODE) {
  15. finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
  16. }
  17. }
  18. return finalDefinition;
  19. }

持续单步调试,代码继续运行到DefaultBeanDefinitionDocumentReader中的processBeanDefinition中的registerBeanDefinition()

  1. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
  2. getReaderContext().getRegistry());

单步进入代码发现BeanDefinitionReaderUtils静态方法registerBeanDefinition()

  1. public static void registerBeanDefinition(
  2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3. throws BeanDefinitionStoreException {
  4. // Register bean definition under primary name.
  5. String beanName = definitionHolder.getBeanName();
  6. // 其实调用的是DefaultListableBeanFactory中的registerBeanDefinition方法
  7. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8. // Register aliases for bean name, if any.
  9. String[] aliases = definitionHolder.getAliases();
  10. if (aliases != null) {
  11. for (String aliase : aliases) {
  12. registry.registerAlias(beanName, aliase);
  13. }
  14. }
  15. }

解释一下其实调用的是DefaultListableBeanFactory中的registerBeanDefinition方法这句话,因为DefaultListableBeanFactory实现BeanDefinitionRegistry接口,BeanDefinitionRegistry接口中定义了registerBeanDefinition()方法

看下DefaultListableBeanFactory中registerBeanDefinition()实例方法的具体实现:

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  2. throws BeanDefinitionStoreException {
  3. Assert.hasText(beanName, "Bean name must not be empty");
  4. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  5. if (beanDefinition instanceof AbstractBeanDefinition) {
  6. try {
  7. ((AbstractBeanDefinition) beanDefinition).validate();
  8. }
  9. catch (BeanDefinitionValidationException ex) {
  10. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  11. "Validation of bean definition failed", ex);
  12. }
  13. }
  14. synchronized (this.beanDefinitionMap) {
  15. Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  16. if (oldBeanDefinition != null) {
  17. if (!this.allowBeanDefinitionOverriding) {
  18. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  19. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  20. "': There is already [" + oldBeanDefinition + "] bound.");
  21. }
  22. else {
  23. if (this.logger.isInfoEnabled()) {
  24. this.logger.info("Overriding bean definition for bean '" + beanName +
  25. "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
  26. }
  27. }
  28. }
  29. else {
  30. this.beanDefinitionNames.add(beanName);
  31. this.frozenBeanDefinitionNames = null;
  32. }
  33. this.beanDefinitionMap.put(beanName, beanDefinition);
  34. resetBeanDefinition(beanName);
  35. }
  36. }

代码追溯之后发现这个方法里,最关键的是以下2行:

  1. this.beanDefinitionNames.add(beanName);
  2. this.beanDefinitionMap.put(beanName, beanDefinition);

前者是把beanName放到队列里,后者是把BeanDefinition放到map中,到此注册就完成了。在后面实例化的时候,就是把beanDefinitionMap中的BeanDefinition取出来,逐一实例化

BeanFactory准备完毕之后,代码又回到了ClassPathXmlApplicationContext里

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. // Prepare this context for refreshing.
  4. prepareRefresh();
  5. // Tell the subclass to refresh the internal bean factory.
  6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  7. // Prepare the bean factory for use in this context.
  8. prepareBeanFactory(beanFactory);
  9. try {
  10. // Allows post-processing of the bean factory in context subclasses.
  11. postProcessBeanFactory(beanFactory);
  12. // Invoke factory processors registered as beans in the context.
  13. invokeBeanFactoryPostProcessors(beanFactory);
  14. // Register bean processors that intercept bean creation.
  15. registerBeanPostProcessors(beanFactory);
  16. // Initialize message source for this context.
  17. initMessageSource();
  18. // Initialize event multicaster for this context.
  19. initApplicationEventMulticaster();
  20. // Initialize other special beans in specific context subclasses.
  21. onRefresh();
  22. // Check for listener beans and register them.
  23. registerListeners();
  24. // Instantiate all remaining (non-lazy-init) singletons.
  25. finishBeanFactoryInitialization(beanFactory);
  26. // Last step: publish corresponding event.
  27. finishRefresh();
  28. }
  29. catch (BeansException ex) {
  30. // Destroy already created singletons to avoid dangling resources.
  31. destroyBeans();
  32. // Reset 'active' flag.
  33. cancelRefresh(ex);
  34. // Propagate exception to caller.
  35. throw ex;
  36. }
  37. }
  38. }

也就是obtainFreshBeanFactory()方法执行之后,再进行下面的步骤。

总结来说,ApplicationContext将解析配置文件的工作委托给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader

BeanDefinitionDocumentReader这个组件是根据xml元素的命名空间和元素名,起到一个路由的作用,实际的解析工作,是委托给BeanDefinitionParserDelegate来完成的

BeanDefinitionParserDelegate的解析工作完成以后,会返回BeanDefinitionHolder给BeanDefinitionDocumentReader,在这里,会委托给DefaultListableBeanFactory完成bean的注册

XmlBeanDefinitionReader(计数、解析XML文档),BeanDefinitionDocumentReader(依赖xml文档,进行解析和注册),BeanDefinitionParserDelegate(实际的解析工作)。可以看出,在解析bean的过程中,这3个组件的分工是比较清晰的,各司其职,这种设计思想值得学习

到此为止,bean的解析、注册、spring ioc 容器的实例化过程就基本分析结束了。

Spring源码剖析2:Spring IOC容器的加载过程的更多相关文章

  1. WorldWind源码剖析系列:星球球体的加载与渲染

    WorldWind源码剖析系列:星球球体的加载与渲染 WorldWind中主函数Main()的分析 在文件WorldWind.cs中主函数Main()阐明了WorldWind的初始化运行机制(如图1所 ...

  2. Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  3. Spring源码分析(十一)bean的加载

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 经过前面的分析,我们终于结束了对XML配置文件的解析,接下来将会面临更大 ...

  4. Spring源码学习(5)—— bean的加载 part 2

    之前归纳了从spring容器的缓存中直接获取bean的情况,接下来就需要从头开始bean的加载过程了.这里着重看单例的bean的加载 if(ex1.isSingleton()) { sharedIns ...

  5. Spring源码解析一:IOC容器设计

    一.IOC接口设计 IOC容器设计的源码主要在spring-beans.jar.spring-context.jar这两个包中.IOC容器主要接口设计如下: 这里的接口设计有两条主线:BeanFact ...

  6. Spring源码解析三:IOC容器的依赖注入

    一般情况下,依赖注入的过程是发生在用户第一次向容器索要Bean是触发的,而触发依赖注入的地方就是BeanFactory的getBean方法. 这里以DefaultListableBeanFactory ...

  7. Spring源码解析二:IOC容器初始化过程详解

    IOC容器初始化分为三个步骤,分别是: 1.Resource定位,即BeanDefinition的资源定位. 2.BeanDefinition的载入 3.向IOC容器注册BeanDefinition ...

  8. spring源码学习之路---IOC容器初始化要义之bean定义载入(五)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 最近工作很忙,时间不多,研究 ...

  9. Spring 源码学习(4)—— bean的加载part 1

    前面随笔中,结束了对配置文件的解析工作,以及将配置文件转换成对应的BeanDefinition存储在容器中.接下来就该进行bean的加载了. public Object getBean(String ...

随机推荐

  1. MFC开发--截图工具

    近期学习了MFC的相关知识,MFC(Microsoft Foundation Classes)是微软公司提供的一个类库,可以这样简单理解,就是对于Win32的封装(MFC对windows API函数的 ...

  2. C#2.0新增功能03 匿名方法

    连载目录    [已更新最新开发文章,点击查看详细] 在 2.0 之前的 C# 版本中,声明委托的唯一方式是使用命名方法. C# 2.0 引入匿名方法,在 C# 3.0 及更高版本中,Lambda 表 ...

  3. thinkphp 数据库操作

    //所有的数据中,查出某个字段$result = $music->field('music')->select();$hotlist = M('News')->where('stat ...

  4. Python 学习笔记(1)Python容器:列表、元组、字典与集合

    Python容器:列表.元组.字典与集合 列表: 1.列表 的创建 使用[ ] 或者 list()创建列表:empty_list = [ ] 或者 empty_list= list() 使用list( ...

  5. Unity3D热更新之LuaFramework篇[08]--热更新原理及热更服务器搭建

    前言 前面铺垫了这么久,终于要开始写热更新了. Unity游戏热更新包含两个方面,一个是资源的更新,一个是脚本的更新. 资源更新是Unity本来就支持的,在各大平台也都能用.而脚本的热更新在iOS平台 ...

  6. PHP xdebug API接口优化揪出了getimagesize这个鬼

    在API优化list中,公司客户系统的服务号客服有个获取聊天消息的接口getHistory请求时间很长,就去优化了下,记下过程. 一,配置环境,追踪使用Xdebug: 1.在https://xdebu ...

  7. maven添加oracle驱动包

    问题描述 项目用到了oracle,但由于oracle商业版权问题,maven在中心资源库直接下载jar包是要收费的 解决方法 第一步: 下载ojdbc6.jar 第二步: 将下载的jar放入项目的li ...

  8. spark shuffle写操作三部曲之BypassMergeSortShuffleWriter

    前言 再上一篇文章 spark shuffle的写操作之准备工作 中,主要介绍了 spark shuffle的准备工作,本篇文章主要介绍spark shuffle使用BypassMergeSortSh ...

  9. 关于HostnameVerifier接口的解读

    在项目中我们需要调用https接口请求.我们使用httpClient,构建HttpClient对象时涉及到使用HostnameVerifier接口实例. HostnameVerifier接口定义如下: ...

  10. javaweb入门-----jsp概念

    jsp是什么? JSP:Java Server Pages java服务器端页面 *可以理解为 一个特殊的页面,其中既可以直接定义html标签,又可以定义java代码 *用于简化书写 <% %& ...