Spring源码分析系列——xml配置非默认元素<context:annotation-config/> 和<context:component-scan/>是如何让注解生效的?

分析方法

在之前的系列文章《Spring源码分析系列----ClassPathXmlApplicationContext加载xml中bean定义注册容器的执行过程分析》中,采用debug断点的方式分析,那篇文章中介绍到分析标签是属于对spring的默认标签解析,那对于非默认的标签是怎么解析的呢?我们以context:annotation-config/和<context:component-scan base-package=“”/>为例来分析一下。

源码分析

《Spring源码分析系列----ClassPathXmlApplicationContext加载xml中bean定义注册容器的执行过程分析》中,我们了解到,解析xml元素是在DefaultBeanDefinitionDocumentReader这个类的parseBeanDefinitions方法,看一下源码

解析xml标签

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;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

解析自定义(非默认)标签

从源码中可以清楚地看到,解析默认元素是在parseDefaultElement方法,解析订制元素委托给delegate的parseCustomElement方法,这个代理类是BeanDefinitionParserDelegate,来看下这个方法的源码

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		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));
	}

简单分析一下,getNamespaceURI方法获得到namespaceUri ,对于标签来说就是xml顶部配置的xmlns:context=“http://www.springframework.org/schema/context”
标签的namespaceUri
通过NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);方法获得NamespaceHandler。如果这个handler不为null,调用它的parse方法。稍后着重分析parse方法。
先看一下这里this.readerContext.getNamespaceHandlerResolver()得到的是DefaultNamespaceHandlerResolver这个类,看下他的resolve()方法

public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				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();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}

通过getHandlerMappings()方法获得一个Map,然后再通过get(namespaceUri)获得Object handlerOrClassName。我们先看下getHandlerMappings方法

getHandlerMappings方法(spring单例模式具体应用)读取配置获得NamespaceHandler?

DefaultNamespaceHandlerResolver的getHandlerMappings方法

private Map<String, Object> getHandlerMappings() {
		if (this.handlerMappings == null) {
			synchronized (this) {
				if (this.handlerMappings == null) {
					try {
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isDebugEnabled()) {
							logger.debug("Loaded NamespaceHandler mappings: " + mappings);
						}
						Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
						this.handlerMappings = handlerMappings;
					}
					catch (IOException ex) {
						throw new IllegalStateException(
								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
					}
				}
			}
		}
		return this.handlerMappings;
	}

这段代码的模式显然是我们很熟悉的单例模式,单例模式变量是用valatile修饰的,我们可以顺便看一下

private volatile Map<String, Object> handlerMappings;

果不其然,handlerMappings是用volatile修饰的。
再具体看代码逻辑
读取this.handlerMappingsLocation的配置文件信息,然后把它放到this.handlerMappings(单例new出来的ConcurrentHashMap)
关键点在于这个配置文件是在哪儿?看下handlerMappingsLocation

private final String handlerMappingsLocation;

什么时候给它赋值的呢?看下DefaultNamespaceHandlerResolver的构造函数

public DefaultNamespaceHandlerResolver() {
		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
	}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
		Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
		this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
		this.handlerMappingsLocation = handlerMappingsLocation;
	}

喔~,原来默认的无参构造函数将DEFAULT_HANDLER_MAPPINGS_LOCATION赋值给handlerMappingsLocation,再看下DEFAULT_HANDLER_MAPPINGS_LOCATION

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

看来是在"META-INF/spring.handlers"这个文件中读取的NamespaceHandler配置信息,
我们看下context包下的这个文件
context包下META-INF/spring.handlers文件

执行NamespaceHandler的init()方法

我们再回到DefaultNamespaceHandlerResolver的resolve()方法,刚才通过getHandlerMappings()方法获得的handlerMappings(一个Map<String, Object>),里有5个键值对,里面就有context标签对应的NamespaceHandler,get方法获得到Object handlerOrClassName,这个handlerOrClassName之所以用Object接收是因为它在第一次获取的时候是string,然后将结果转化为NamespaceHandler再放入handlerMappings里,之后就直接获取NamespaceHandler。
再来看一遍源码就非常清楚了

@Override
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				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();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}

第一次会进入else里面,第二次进入else if里面,我们看下else里面的代码。通过反射获取具体的NamespaceHandler,然后执行namespaceHandler.init();

执行NamespaceHandler的parse()方法

init()方法执行完后,返回namespaceHandler,继续执行代理类BeanDefinitionParserDelegate的parseCustomElement的后续方法,来看一下

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		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));
	}

执行handler.parse()方法

解析ContextNamespaceHandler

这个类就是配置在META-INF/spring.handlers下的其中一个NamespaceHandler,是专门用来解析xml中配置标签的NamespaceHandler来看下源码

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
		registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
		registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
		registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
	}

}

很简单,只有一个init()方法。上面已经分析过,会先执行NamespaceHandler的init()方法,再解析parse()方法,看下继承结构
ContextNamespaceHandler的类继承结构

ContextNamespaceHandler的init()方法

init()方法通过registerBeanDefinitionParser方法添加了很多具体的parser,看下这个方法把这些parser放到哪儿了

在父类NamespaceHandlerSupport

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}
private final Map<String, BeanDefinitionParser> parsers =
			new HashMap<String, BeanDefinitionParser>();

放到了一个HashMap中

ContextNamespaceHandler的parse()方法

在父类NamespaceHandlerSupport

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

这个localName就是“component-scan”,通过parsers.get(localName)得到刚才注册进去的ComponentScanBeanDefinitionParser,再调用这个parser的parse()方法,终于找到了,我们来看下它的源码

“component-scan” 最终真正执行的是ComponentScanBeanDefinitionParser的parse()方法
@Override
	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);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}

关键代码scanner.doScan(basePackages);这个方法就会去扫描指定包basePackages下加了@component等注解的类了,将他们注册到DefaultListableBeanFactory。这个我们在
Spring源码分析系列——AnnotationConfigApplicationContext(String… basePackages)扫描加载注解bean这篇文章中分析过了。
再看registerComponents()方法,

protected void registerComponents(
			XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {

		Object source = readerContext.extractSource(element);
		CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);

		for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
			compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
		}

		// Register annotation config processors, if necessary.
		boolean annotationConfig = true;
		if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
			annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
		}
		if (annotationConfig) {
			Set<BeanDefinitionHolder> processorDefinitions =
					AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
			for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
				compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
			}
		}

		readerContext.fireComponentRegistered(compositeDef);
	}

annotationConfig默认为true
所以会执行
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);这个方法,这个方法注册的是ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等BeanPostProcessor、BeanFactoryPostProcessor,用来处理@configuration、@autowire等注解
这个也在之前文章Spring源码分析系列——AnnotationConfigApplicationContext(String… basePackages)扫描加载注解bean分析过。

“annotation-config” 最终真正执行的是AnnotationConfigBeanDefinitionParser的parse()方法

类比“component-scan”,AnnotationConfigBeanDefinitionParser的parse()执行的就是
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
方法,就是添加注解的核心内部BeanPostProcessor、BeanFactoryPostProcessor。
到此,解析结束。

总结

核心流程如下:

1、delegate.parseCustomElement(ele) :解析xml自定义标签====>
2、this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri):读取META-INF/spring.handlers配置文件,通过自定义标签的namespaceUri获得具体的NamespaceHandler====>
3、namespaceHandler.init():调用NamespaceHandler的init()方法====>
4、handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)):调用namespaceHandler的parse()方法
5、ComponentScanBeanDefinitionParser等具体parser的parse()方法


版权声明:本文为Maybe_9527原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。