XML标签解析过程
从ClassPathXmlApplicationContext.refresh()方法进入
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
  prepareRefresh();
   ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  .........
}

进入obtainFreshBeanFactory(),重点看 refreshBeanFactory()

初始化BeanFactory ,将xml文件中的标签封装成BeanDefinition

 

@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果BeanFactory不为空,则清除BeanFactory和里面的实例
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//BeanFactory 实例工厂
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//设置是否可以循环依赖 allowCircularReferences
			//是否允许使用相同名称重新注册不同的bean实现.
			customizeBeanFactory(beanFactory);
			//解析xml,并把xml中的标签封装成BeanDefinition对象
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

 

继续往下看,如何把标签解析封装成BeanDefinition

在loadBeanDefinitions方法中创建xml的解析器,将解析器对象传进去,从loadBeanDefinitions()一直点下去,开始解析xml文件

 

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
....
....
beanDefinitionReader.setResourceLoader(this);//解析器中传入this进去,因为ApplicationContext是实现了ResourceLoader接口的
loadBeanDefinitions(beanDefinitionReader);

 

获取遍历所有的xml文件

 

String[] configLocations = getConfigLocations();//获取本地xml文件
if (configLocations != null) {
   reader.loadBeanDefinitions(configLocations);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		................
		if (resourceLoader instanceof ResourcePatternResolver) {
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//主要看这个方法
				int count = loadBeanDefinitions(resources);
	     ...................
	}

  

一直点下去,到 XmlBeanDefinitionReader类,看loadBeanDefinitions方法

 

//获取Resource对象中的xml文件流对象
		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {

			//InputSource是jdk中的sax xml文件解析对象
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			//主要看这个方法
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}

  

重点来了

do方法点进去,看 registerBeanDefinitions()方法,根据解析出来的doc对象,拿到里面的标签

 

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();

		//createReaderContext(resource)封装了XmlBeanDefinitionReader对象
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

  

继续往下看,通过doc拿到root节点,看 parseBeanDefinitions()方法,遍历解析标签,默认标签和自定义标签

默认标签解析

 

parseDefaultElement(ele, delegate);

 

重点看我们熟悉的bean标签,processBeanDefinition方法点进去,看parseBeanDefinitionElement方法

 

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);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

  

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);解析标签,封装成bd


方法点进去,就不贴代码了,就是拿到标签里的id、name、class、singleton、scope、lazy-init、depends-on、factory-method、factory-bean等属性值,很多,就不一一写出来了

记住factory-method、factory-bean这两个属性,@Bean注解就是用的这个思路

几乎所有的注解的实现都可以和传统的xml标签对应上

 

再回过头看自定义标签的解析,先这个方法

 

delegate.parseCustomElement(ele);

 

 

自定义标签要比默认标签要复杂一些

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);获取自定义标签的 namespace 命令空间,什么是namespaceUri,就是我们xml文件最上面的引入的“http://www.springframework”这段东西
if (namespaceUri == null) {
			return null;
		}
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

 

resolve方法点进去看

public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();//获取spring所有jar包里面“META-INF/spring.handlers”文件,并建立映射关系
		Object handlerOrClassName = handlerMappings.get(namespaceUri);//根据uri获取到这个命名空间的处理类
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;//获取classname
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);//拿到这个class
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//实例化这个处理类
				namespaceHandler.init();//执行他的init方法,下面截个图看看有都有哪些
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			......................
		}
	}

 

 

看到这个图,都熟悉的aop、jdbc、mvc等处理类

spring orc 文件解析_封装

 

 

我们看一下处理类里面是些什么东西,熟悉的标签,registerBeanDefinitionParser方法会把标签及对应的处理类方法一个map中

spring orc 文件解析_封装_02

 

 

回头来看调用parse方法,看一下component-scan标签是怎么解析的,我们常说的扫描就是从这里开始的

public BeanDefinition parse(Element element, ParserContext parserContext) {
		String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);//获取标签值
		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);//可以以逗号分开

		// Actually scan for bean definitions and register them.
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);//创建注解扫描器,点进去可以看到,添加了@component注解的过滤集合,贴个截图
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);//doscan执行扫描
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);//执行一些比较重要的后置处理

		return null;
	}

  

spring orc 文件解析_自定义标签_03

 

 

 后面方法体代码比较多,就不贴很全了

比较重要的doscan方法,扫描包路径下所有的class文件,并过滤出@service等注解的类,所有继承了@component的注解

 

 

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//扫描有注解的类,并封装成bd对象,主要是拿到包路径下所有的class文件,并match判断一下有没有过滤集合中的注解,不点进去看了
              //只需注意一点,这里的bd是scannedGenericBeanDefinition,其中封装了class的一些元数据,包括注解信息等,match注解以及下面的注解支持都基于元数据
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());//设置scope值
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//获取beanName,如果注解中没有指定,则取className
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);//支持了@Lazy、dependsOn等注解,可以点进去看一下
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//封装成熟悉的bdh对象
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);//注册,和上面说到的默认标签同样
				}
			}
		}
		return beanDefinitions;
	}

  

 自定义标签的bd对象收集已经结束了,这里只是以component-scan为例。

回过头看一下非常重要的registerComponents方法,这几个后置处理器后面再说

方法里面注册了几个比较重要的如 ConfigurationClassPostProcessor(会扫描@Configuration @Component @Bean 注解的解析)、      AutowiredAnnotationBeanPostProcessor(会扫描@Value 和@Autowired 注解)、

CommonAnnotationBeanPostProcessor

 

到这里为止,我们xml解析已经完成了,并封装成了bd对象,下一步就是要把bd对象注册到我们的BeanFactory中

再回到 processBeanDefinition()方法中,看下面这个方法

 

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

 

看最后的一段代码

// Still in startup registration phase
	this.beanDefinitionMap.put(beanName, beanDefinition);
	this.beanDefinitionNames.add(beanName);

把beanName作为key,bd作为value,放到db的map中

把beanName放到beanDefinitionNames 集合中,这个list要记住,bean实例化的时候需要用到

 

到这里,xml标签的解析就结束了