Spring之BeanFactory源码分析(二)

前言

在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,AutowireCapableBeanFactory。主要核心类DefaultListableBeanFactory,通过编程启动IOC容器 将BeanFactory的功能逐渐的剥离开来,方便我们理解整个架构。

  1. ClassPathResource resource = new ClassPathResource("spring.xml");
  2. DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
  3. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
  4. reader.loadBeanDefinitions(resource);
  5. MyBean bean = factory.getBean(MyBean.class);
  6. System.out.println(bean.toString());

DefaultListableBeanFactory 实现了 BeanDefinitionRegistry接口,具有了注册bean的功能,

读取资源则通过单独的模块来实现,这里委托为XmlBeanDefinitionReader来读取xml配置文件

ApplicationContext

在前面我们可以很方便的通过编程的方式来手工控制这些配置的容器的建立过程了,但是,在Spring 中,系统以及为我们提供了许多已经定义好的容器的实现,如果说BeanFactory是Spring的"心脏",那么ApplicationContext就是完整的"身躯"了。ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能,所以说,ApplicationContext是一个高级形态意义的IOC容器,下面我们就来慢慢的认识一下它。

再来看一段代码:

  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
  2. MyBean bean = context.getBean(MyBean.class);
  3. System.out.println(bean.toString());

这个看起来是不是比 DefaultListableBeanFactory 简单多了,其实所谓的高级容器也就是把最基础的容器进行了封装,因此在最开始时我们才使用最基础的BeanFactory来展示,这样更容易理解。

体系结构

  • 支持不同的信息源,我们看到ApplicationContext 扩展的MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。
  • 访问资源。这一特性体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方得到Bean定义资源。这种抽象使 用户可以灵活的定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。
  • 支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。

看到如上的继承体系,应该就更能明白ApplicationContext 是Spring BeanFactory的高级形态的容器了。

接口方法

  1. @Nullable
  2. String getId();
  3. /**
  4. * Return a name for the deployed application that this context belongs to.
  5. * @return a name for the deployed application, or the empty String by default
  6. */
  7. String getApplicationName();
  8. /**
  9. * Return a friendly name for this context.
  10. * @return a display name for this context (never {@code null})
  11. */
  12. String getDisplayName();
  13. /**
  14. * Return the timestamp when this context was first loaded.
  15. * @return the timestamp (ms) when this context was first loaded
  16. */
  17. long getStartupDate();
  18. /**
  19. * Return the parent context, or {@code null} if there is no parent
  20. * and this is the root of the context hierarchy.
  21. * @return the parent context, or {@code null} if there is no parent
  22. */
  23. @Nullable
  24. ApplicationContext getParent();
  25. /**
  26. * Expose AutowireCapableBeanFactory functionality for this context.
  27. * <p>This is not typically used by application code, except for the purpose of
  28. * initializing bean instances that live outside of the application context,
  29. * applying the Spring bean lifecycle (fully or partly) to them.
  30. * <p>Alternatively, the internal BeanFactory exposed by the
  31. * {@link ConfigurableApplicationContext} interface offers access to the
  32. * {@link AutowireCapableBeanFactory} interface too. The present method mainly
  33. * serves as a convenient, specific facility on the ApplicationContext interface.
  34. * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
  35. * after the application context has been closed.</b> In current Spring Framework
  36. * versions, only refreshable application contexts behave that way; as of 4.2,
  37. * all application context implementations will be required to comply.
  38. */
  39. AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

在ApplicationContext容器中,我们以常用的ClassPathXmlApplicationContext的实现来分析ApplicationContext容器的设计原理

ClassPathXmlApplicationContext

体系结构

在ClassPathXmlApplicationContext 的设计中,其主要的功能在基类AbstractXmlApplicationContext中已经实现了。

源码分析

  1. public ClassPathXmlApplicationContext(
  2. String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
  3. throws BeansException {
  4. super(parent);
  5. setConfigLocations(configLocations);
  6. if (refresh) {
  7. refresh();
  8. }
  9. }

ClassPathXmlApplicationContext 看起来还是很简单的,主要的实现都在基类里面实现了,这是只是负责调用。

父容器的设置在 AbstractApplicationContext

  1. /** Parent context */
  2. @Nullable
  3. private ApplicationContext parent;
  4. @Override
  5. public void setParent(@Nullable ApplicationContext parent) {
  6. this.parent = parent;
  7. if (parent != null) {
  8. Environment parentEnvironment = parent.getEnvironment();
  9. if (parentEnvironment instanceof ConfigurableEnvironment) {
  10. getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
  11. }
  12. }
  13. }

如果存在父容器,那么会合并两者的环境配置Environment。这里Environment并不是我们的重点。

这个 refresh()过程会牵涉IOC容器启动的一系列复杂操作,同时,对于不同容器的实现,这些操作都是类似的,因此在基类中将它们封装好。所以,我们在ClassPathXmlApplicationContext 的设计中看到的只是一个简单的调用。关于这个refresh()在IOC容器启动时的具体实现,这个在后面再来分析,这里就不展开了。

IOC容器的初始化过程

简单来说,IOC容器的初始化由前面介绍的refresh()方法来启动的,这个方法标志着IOC容器的正式启动。具体来说,这个启动包括BeanDefinition的Resouce定位、载入和注册三个基本过程。如果我们了解如何编程式地使用IOC容器,就可以清楚得看到Resource定位和载入过程的接口调用。在下面的内容里,我们将会详细分析这三个过程的实现。

Spring把这三个过程分开,并使用不同的模块来完成,如使用响应的ResourceLoader、BeanDefinitionReader等模块,通过这样的设计方式,可以让用户更加灵活地对这三个过程进行剪裁或扩展,定义出最适合自己的IOC容器的初始化过程。

  • Resource 定位

    第一个过程就是Resource 定位过程。这个Resource 定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。

  • BeanDefinition的载入

    第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IOC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition,具体来说,这个BeanDefinition实际上就是POJO对象在IOC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IOC容器能够方便地对POJO对象也就是Bean进行管理。

  • 注册BeanDefinition

    第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IOC容器进行注册,通过分析,我们可以看到,在IOC容器内部将BeanDefinition注入到一个ConcurrentHashMap中去,IOC容器就是通过这个HashMap来持有这些BeanDefinition数据的。

BeanDefinition的Resource定位

以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。

  1. ClassPathResource resource = new ClassPathResource("spring.xml");

这里定义的Resource 并不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。因为ApplicationContext 中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IOC容器,需要为它配置特定的读取器才能完成这些功能。当然,有利就有弊,使用DefaultListableBeanFactory 这种更底层的容器,能提高定制IOC容器的灵活性。

下面以ClassPathXmlApplicationContext 为例,通过分析这个ApplicationContext的实现来看看它是怎样完成这个Resource定位过程的。

这个ClassPathXmlApplicationContext 已经通过继承AbstractApplicationContext具备了ResourceLoader读入Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是DefaultResourceLOader。

在前面ClassPathXmlApplicationContext 的源码中,我们知道核心代码在refresh方法里面。下面是refresh的代码,但是并不会详细分析,我们的工作主要还是拆,目前只分析我们需要的。

AbstractApplicationContext -> refresh():

  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. if (logger.isWarnEnabled()) {
  31. logger.warn("Exception encountered during context initialization - " +
  32. "cancelling refresh attempt: " + ex);
  33. }
  34. // Destroy already created singletons to avoid dangling resources.
  35. destroyBeans();
  36. // Reset 'active' flag.
  37. cancelRefresh(ex);
  38. // Propagate exception to caller.
  39. throw ex;
  40. }
  41. finally {
  42. // Reset common introspection caches in Spring's core, since we
  43. // might not ever need metadata for singleton beans anymore...
  44. resetCommonCaches();
  45. }
  46. }
  47. }

refresh 里面就是Spring的启动流程,在 refresh -> obtainFreshBeanFactory 会创建一个BeanFactory,而obtainFreshBeanFactory在子类AbstractRefreshableApplicationContext中实现。

  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. //载入BeanDefinition,其余的方法暂时不分析
  11. loadBeanDefinitions(beanFactory);
  12. synchronized (this.beanFactoryMonitor) {
  13. this.beanFactory = beanFactory;
  14. }
  15. }
  16. catch (IOException ex) {
  17. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  18. }
  19. }

在这个方法中,通过 createBeanFactory 构建一个IOC容器供ApplicationContext 使用。这个IOC容器就是我们前面提到过的DefaultListableBeanFactory,同时,它启动了loadBeanDefinitions 来载入BeanDefinition,这个过程和前面用编程式的方法来使用IOC(XmlBeanFactory)的过程非常类似。

AbstractXmlApplicationContext ->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. }

设置BeanDefinitionReader,因为AbstractApplicationContext 继承DefaultResourceLoader ,因此ResourceLoader 可以设置成this,继续跟踪代码,在 AbstractBeanDefinitionReader -> loadBeanDefinitions中:

  1. public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
  2. ResourceLoader resourceLoader = getResourceLoader();
  3. if (resourceLoader instanceof ResourcePatternResolver) {
  4. // Resource pattern matching available.
  5. try {
  6. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  7. //在载入和解析中分析
  8. int loadCount = loadBeanDefinitions(resources);
  9. if (actualResources != null) {
  10. for (Resource resource : resources) {
  11. actualResources.add(resource);
  12. }
  13. }
  14. return loadCount;
  15. }
  16. catch (IOException ex) {
  17. //...省略
  18. }
  19. }
  20. else {
  21. //...省略代码
  22. }
  23. }
  1. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

通过 resourceLoader来获取配置资源,而这个resourceLoader 就是 ClassPathXmlApplicationContext,这个getResources 方法在父类AbstractApplicationContext中实现

AbstractApplicationContext -> getResources:

  1. public Resource[] getResources(String locationPattern) throws IOException {
  2. return this.resourcePatternResolver.getResources(locationPattern);
  3. }

resourcePatternResolver 在初始化的时候,被设置成了 PathMatchingResourcePatternResolver

  1. public AbstractApplicationContext() {
  2. this.resourcePatternResolver = getResourcePatternResolver();
  3. }
  4. protected ResourcePatternResolver getResourcePatternResolver() {
  5. return new PathMatchingResourcePatternResolver(this);
  6. }

这样就可以通过PathMatchingResourcePatternResolver 来获取资源了。

BeanDefinition的载入和解析

在完成BeanDefinition的Resource定位的分析后,下面来了解整个BeanDefinition信息的载入过程。对IOC容器来说,这个载入过程,相当于把定义的BeanDefinition在IOC容器中转化成一个Spring内部表示的数据结构的过程。IOC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在IOC容器中通过一个HashMap来保持和维护。

在前面,我们定位资源的时候,展示了AbstractBeanDefinitionReader 中的loadBeanDefinitions 方法,在里面会调用下面的代码:

  1. int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

但这个方法在AbstractBeanDefinitionReader 类里是没有实现的,它是一个接口方法,具体的实现在 XmlBeanDefinitionReader中,在读取器中,需要得到代表XML文件的Resource,因为这个Resource对象封装了对XML文件的I/O操作,所以读取器可以在打开I/O流后得到XML的文件对象,有了这个对象文件以后,就可以按照Spring的Bean定义规则来对这个XML的文档树进行解析了,这个解析是交给BeanDefinitionParserDelegate来完成的。

XmlBeanDefinitionReader -> loadBeanDefinitions:

  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  3. if (currentResources == null) {
  4. currentResources = new HashSet<>(4);
  5. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  6. }
  7. if (!currentResources.add(encodedResource)) {
  8. //省略代码
  9. }
  10. try {
  11. InputStream inputStream = encodedResource.getResource().getInputStream();
  12. try {
  13. InputSource inputSource = new InputSource(inputStream);
  14. if (encodedResource.getEncoding() != null) {
  15. inputSource.setEncoding(encodedResource.getEncoding());
  16. }
  17. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  18. }
  19. finally {
  20. inputStream.close();
  21. }
  22. }
  23. catch (IOException ex) {
  24. //省略代码
  25. }
  26. finally {
  27. //省略代码
  28. }
  29. }

接着看doLoadBeanDefinitions 方法:

  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2. throws BeanDefinitionStoreException {
  3. try {
  4. //获取XML文件的Document对象
  5. Document doc = doLoadDocument(inputSource, resource);
  6. //对BeanDefinition解析的过程
  7. return registerBeanDefinitions(doc, resource);
  8. }
  9. //省略部分代码
  10. }

这里我们就不去分析如何得到Document对象的了,我们关心的是Spring的BeanDefinion是怎么按照Spring的Bean语义要求进行解析并转化为容器内部数据机构的,这个过程是在registerBeanDefinitions 中完成的。

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. //通过 BeanDefinitionDocumentReader 对XML的BeanDefinition 进行解析
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. int countBefore = getRegistry().getBeanDefinitionCount();
  5. //具体解析过程
  6. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  7. return getRegistry().getBeanDefinitionCount() - countBefore;
  8. }

BeanDefinition的载入分成两部分,首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析。在完成通用的XML解析过后,才是按照Spring的Bean规则进行解析的地方,这个按照Spring的Bean规则进行解析的过程是在documentReader中实现的,这里使用的documentReader是默认配置好的DefaultBeanDefinitionDocumentReader。

  1. protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
  2. return BeanUtils.instantiateClass(this.documentReaderClass);
  3. }
  1. private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
  2. DefaultBeanDefinitionDocumentReader.class;

在 DefaultBeanDefinitionDocumentReader-> 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. }

对bean的配置的解析处理是通过processBeanDefinition 方法

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. // 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中
  3. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  4. if (bdHolder != null) {
  5. // 如果有自定义属性的话,进行相应的解析,先忽略
  6. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  7. try {
  8. //这里是向IOC容器注册BeanDefinition
  9. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  10. }
  11. catch (BeanDefinitionStoreException ex) {
  12. //省略代码
  13. }
  14. //在BeanDefinition向IOC容器注册完后,发送消息
  15. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  16. }
  17. }

提取出来的信息结果由BeanDefinitionHolder对象来持有。这个BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition的使用相关的信息,比如Bean的名字,别名集合等。

  1. public class BeanDefinitionHolder implements BeanMetadataElement {
  2. private final BeanDefinition beanDefinition;
  3. private final String beanName;
  4. private final String[] aliases;
  5. ...

具体的Spring BeanDefinition的解析是在BeanDefinitionParserDelegate 中完成的,这个类包含了对各种Spring Bean 定义的规则的处理,这里我们暂且就不深入了。

BeanDefinition在IOC容器中的注册

前面已经分析过了BeanDefinition在IOC容器中载入和解析的过程。在这些动作完成以后,用户定义的BeanDefinition信息已经在IOC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IOC容器直接使用,需要在IOC容器中对这些BeanDefinition数据进行注册,这个注册为IOC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通过一个ConcurrentHashMap来持有载入的BeanDefinition的。

在DefaultBeanDefinitionDocumentReader ->processBeanDefinition 中通过如下代码注册:

  1. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  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. // registry 就是 DefaultListableBeanFactory
  7. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8. // 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了
  9. String[] aliases = definitionHolder.getAliases();
  10. if (aliases != null) {
  11. for (String alias : aliases) {
  12. // alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,
  13. // 获取的时候,会先将 alias 转换为 beanName,然后再查找
  14. registry.registerAlias(beanName, alias);
  15. }
  16. }
  17. }

查看 DefaultListableBeanFactory 中的 registerBeanDefinition 方法:

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  2. throws BeanDefinitionStoreException {
  3. //省略了部分代码
  4. // 检查BeanDefinition 是否已经存在
  5. BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
  6. if (existingDefinition != null) {
  7. //如果不允许覆盖
  8. if (!isAllowBeanDefinitionOverriding()) {
  9. //抛异常
  10. }
  11. else if (existingDefinition.getRole() < beanDefinition.getRole()) {
  12. //可参考 BeanFactory 源码分析(1)中的BeanDefinition分析
  13. //用框架定义的 Bean 覆盖用户自定义的 Bean
  14. }
  15. else if (!beanDefinition.equals(existingDefinition)) {
  16. //用新的 Bean 覆盖旧的 Bean
  17. }
  18. else {
  19. //用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
  20. }
  21. // 覆盖
  22. this.beanDefinitionMap.put(beanName, beanDefinition);
  23. }
  24. else {
  25. // 判断是否已经有其他的 Bean 开始初始化了.
  26. // 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化
  27. if (hasBeanCreationStarted()) {
  28. // Cannot modify startup-time collection elements anymore (for stable iteration)
  29. synchronized (this.beanDefinitionMap) {
  30. this.beanDefinitionMap.put(beanName, beanDefinition);
  31. List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
  32. updatedDefinitions.addAll(this.beanDefinitionNames);
  33. updatedDefinitions.add(beanName);
  34. this.beanDefinitionNames = updatedDefinitions;
  35. if (this.manualSingletonNames.contains(beanName)) {
  36. Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
  37. updatedSingletons.remove(beanName);
  38. this.manualSingletonNames = updatedSingletons;
  39. }
  40. }
  41. }
  42. else {
  43. // 一般会进到这个分支。
  44. // 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinition
  45. this.beanDefinitionMap.put(beanName, beanDefinition);
  46. // 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
  47. this.beanDefinitionNames.add(beanName);
  48. // 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
  49. // 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
  50. // 手动指的是通过调用以下方法注册的 bean :
  51. // registerSingleton(String beanName, Object singletonObject)
  52. // 这不是重点,Spring 会在后面"手动"注册一些 Bean,
  53. // 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的
  54. this.manualSingletonNames.remove(beanName);
  55. }
  56. this.frozenBeanDefinitionNames = null;
  57. }
  58. }

省略了部分代码,这里主要展示的大致流程,对于具体的细节这里并没有涉及。现在我们已经分析完BeanDefinition的载入和注册了,此时依赖注入并没有发生,依赖注入发生在应用第一次向容器索要bean时,当然可以设置Bean的lazy-init属性来控制预实例化的过程,这里我们并不会分析依赖注入过程,只是大致梳理了IOC容器的初始化过程,后面会再次深入这部分,一点一点的解剖。

总结

在分析了底层的BeanFactory后,我们分析了高级形态的BeanFactory-ApplicationContext,ApplicationContext其实就是将其他功能集成了起来,使得BeanFactory不仅仅是一个容器了,ApplicationContext具有了下面的特性:

  • 支持不同的信息源
  • 访问资源
  • 支持应用事件

随后我们以ClassPathXmlApplicationContext为例简单的分析了BeanDefinition的资源定位,载入和解析以及注册过程。

  • 通过PathMatchingResourcePatternResolver 来进行定位。
  • XmlBeanDefinitionReader 读取XML,再通过 BeanDefinitionDocumentReader 读取配置信息,将配置信息放入 BeanDefinitionHolder 中。
  • 在DefaultListableBeanFactory 中注册这些 BeanDefinition。

十、Spring之BeanFactory源码分析(二)的更多相关文章

  1. 九、Spring之BeanFactory源码分析(一)

    Spring之BeanFactory源码分析(一) ​ 注意:该随笔内容完全引自https://blog.csdn.net/u014634338/article/details/82865644,写的 ...

  2. Spring Developer Tools 源码分析:二、类路径监控

    在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...

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

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

  4. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

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

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

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

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

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

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

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

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

  9. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

随机推荐

  1. 11-Django站点管理

    站点管理 内容发布的部分由网站的管理员负责,包括查看.添加.修改.删除数据 开发这些重复的功能是一件单调乏味.缺乏创造力的工作,为此,Django能够根据定义的模型类自动地生成管理模块 在Django ...

  2. go语言中map每次遍历的顺序不同-问题分析

    WHAT? 发现下面这段代码,多次运行出的结果是不一样的 mapper := make(map[int]string) mapper[1] = "1" mapper[2] = &q ...

  3. WPF 启动页面 (原发布 csdn 2017-06-26 19:26:01)

    如果我写的有误,请及时与我联系,我立即改之以免继续误导他/她人. 如果您有好的想法或者建议,请随时与我联系. wpf软件启动时,加载启动页面.软件初始化完成之后关闭页面. App.xaml.cs代码 ...

  4. TreeMap源码分析,看了都说好

    概述 TreeMap也是Map接口的实现类,它最大的特点是迭代有序,默认是按照key值升序迭代(当然也可以设置成降序).在前面的文章中讲过LinkedHashMap也是迭代有序的,不过是按插入顺序或访 ...

  5. CSS filter滤镜试玩

    1.模糊(blur). 用法:给相应元素设置高斯模糊,传入的px数值越大越模糊. 2.亮度(brightness). 用法:给元素设置亮度,0%为全黑,100%为元素原始亮度,>100%表示会比 ...

  6. Linux域名服务DNS

    什么是 DNS DNS 全称是 Domain Name System,大意是域名解析系统,它的职责是把域名翻译成一个一个可以识别的 IP 供不同的计算机设备连接. linux 有关 DNS 解析的配置 ...

  7. 「SAP技术」SAP 如何看序列号被包在哪些HU里?

    「SAP技术」SAP 如何看序列号被包在哪些HU里? 事务代码SE16 ,表名OBJK, 输入物料号,序列号,HeadTable 输入值SER06, 查询结果如下, 根据objlist, 去表ser0 ...

  8. JavaScriptCore

    在移动的混合开发中经常用到OC与JS的交互,就涉及iOS中的JavaScriptCore类,下面终结如下 JavaScriptCore中的类 在项目中引入JavaScriptCore后,链到头文件中, ...

  9. 重启电脑 wamp图标是橙色(未变绿)

    记录一个错误: 修复系统漏洞后,重启电脑,wamp没有开机自启动,手动启动后发现,图标是大红色变成了橙色,也就是服务未完全启动(1/2)状态. ??? 但是我其实也不知道是哪个服务(Apache/My ...

  10. 怎么更改當前的USERENV('LANG')返回值

    [php] SQL> ALTER SESSION SET NLS_LANGUAGE='AMERICAN'; Session altered. SQL> select USERENV('LA ...