Spring源码私家笔记——Bean的元信息BeanDefinition加载流程

我们知道Spring的可用通过多种方式进行配置:XML配置文件、Groovy配置文件、注解配置、Java代码配置。无论什么样的形式的配置都要先被解析成初始化Bean所需要的各种元信息(Metadata),也就是BeanDefinition对象。

上一个回合,我们提到BeanFactory已经被创建了,那紧接着,我们看到BeanDefinition就进行加载了,我们对着核心源码一一道来。
目前,我们就已经得到了一个BeanFactory对象,这之后,我们将使用BeanFactory实例完成一系列的后续工作。在refreshBeanFactory中,则有非常重要的一步——loadBeanDefinitions(beanFactory),这里面的源码真的是很庞大,我们还是挑重点进行讲解。
在这里插入图片描述

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 为BeanFactory实例创建一个XmlBeanDefinitionReader实例,实际的BeanDefinition便是交给该实例来完成的
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // 将Environment/资源加载器/XML的实体解析器设置到XmlBeanDefinitionReader实例上
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // 执行核心的BeanDefinition加载逻辑
   initBeanDefinitionReader(beanDefinitionReader);
   loadBeanDefinitions(beanDefinitionReader);
}

继续看 loadBeanDefinitions(beanDefinitionReader)的核心逻辑

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

下面就进入了XmlBeanDefinitionReader的加载逻辑中

@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
   Assert.notNull(locations, "Location array must not be null");
   int count = 0;
   for (String location : locations) {
      count += loadBeanDefinitions(location);
   }
   return count;
}

加载多个资源,便有加载单个资源接口,直接看

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   ResourceLoader resourceLoader = getResourceLoader();
   // ignore

   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         int count = loadBeanDefinitions(resources);
         if (actualResources != null) {
            Collections.addAll(actualResources, resources);
         }
         // ignore
         return count;
      }
      catch (IOException ex) {
         // ignore
      }
   }
   else {
      // ignore
   }
}

转化成String location转化成 Resource之后继续下钻 - loadBeanDefinitions(resources)

并且会继续遍历后执行 loadBeanDefinitions(new EncodedResource(resource));

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   // ignore

   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   // ignore
   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());
      }
      // ignore
   }
   // ignore
}

EncodedResource加上了编码和字符集信息,继续下钻至 doLoadBeanDefinitions(inputSource, encodedResource.getResource())

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {

   try {
      Document doc = doLoadDocument(inputSource, resource);
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
   }
  // ignore
}

这里加上2个标题,之前都跳过也没有关系,核心的就是这里的逻辑:

将配置信息Resource读取成Document对象中,并根据该Document对象将资源注册到Bean工厂中

  • doLoadDocument(inputSource, resource)
  • registerBeanDefinitions(doc, resource)
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 专门处理
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  	// registry就是BeanFactory(实现了BeanDefinitionRegistry)
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

继续看,又是将处理逻辑委托给BeanDefinitionDocumentReader实例的方法 registerBeanDefinitions,搞不清这是装饰器模式,还是适配器模式,还是代理模式。。。

先看DefaultBeanDefinitionDocumentReader的实现吧

protected void doRegisterBeanDefinitions(Element root) {
   // ignore
   this.delegate = createDelegate(getReaderContext(), root, parent);
  
   preProcessXml(root);
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);

   this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   // <bean 使用的是默认的namespace,进入该分支
   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)) {
               // 看这儿
               parseDefaultElement(ele, delegate);
            }
            else {
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      // ignore
   }
}

又是委托给BeanDefinitionParserDelegate来干剩下的活,是一层层做增强,但也不是装饰器模式吧,没有统一的契约额。算了,继续向下看吧

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      // 太多了,重点看下<bean Node的加载逻辑完事了
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  // 这儿重点,document解析成BeanDefinition就是这儿完成的! 
  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // 又继续向下交给BeanDefinitionReaderUtils工具类的静态方法来处理额
         // 参数就是BeanDefinition的持有器和BeanFactory这个BeanDefintion注册器
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      // ignore
   }
}

进去看下

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // 狗日的,兜一大圈回来,还是交给BeanFactory这个BeanDefintion注册器来完成注册
   String beanName = definitionHolder.getBeanName();
   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);
      }
   }
}

我们在上面铁锅BeanFactory的类图,实现了BeanDefinitionRegistry和AliasRegistry,所以传进来的就是BeanFactory对象了

这样兜一大圈子有啥好处呢?这样理解起来也贼复杂了

这就是面向接口编程了,每一层只负责自己职责内的逻辑,其他的逻辑我只调用接口引用的方法就可以了,你给我什么实现,就会调用谁的实现。扩展性就好了嘛

因为我们知道不同的环境下,很多的变话,比如:

  1. 配置文件的读取逻辑不同,所以要用不同的Reader
  2. BeanFactory的具体实现类也可能有所不同

回到BeanFactory再看,BeanDefinition是怎么被加载的,进入org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		// ignore
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			// ignore
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			// ignore
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
          // 维护一个MAP beanName to beanDefinition
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
          // 所有的beanDefinition名称
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

这里的逻辑就蛮简单的,BeanDefinition列表都已经拿到了,就注册上去好了(忽略的和没有提到的代码无非就是检查重复、判断是否已经有同名的单例Bean存在了,都销毁、刷新或重置下)

至此,就完成了BeanFactory的实例化,基础设置工作和BeanDefinition加载工作,因为笔者使用的是XML的配置文件,在最终的BeanDefinition加载前经过XmlBeanDefinitionReader中的BeanDefinitionDocumentReader处理,并交由BeanDefinitionParserDelegate完成配置资源加载成Document并解析成BeanDefinition,并由BeanDefinitionReaderUtils调用BeanDefinitionRegistry实例(也就是BeanFactory实例)完成BeanDefinition的注册。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值