分析方法
在之前的系列文章《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”
通过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包下的这个文件
执行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的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()方法