一、配置加载
每个程序启动都要加载配置,只是不同程序读取配置方式不同,spring也有一套自己规则的配置方式,spring通过beanFactory来加载配置、管理对象,BeanFactory子类树是非常复杂的,如果每一个都看非常耗时间,可以找一个典型的子类看一下它的初始化过程,以XmlBeanFactory为例看spring加载配置。
看XmlBeanFactory的入口:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
通过XmlBeanDefinitionReader 加载配置资源, 开始跟踪loadBeanDefinitions的代码,发现XmlBeanDefinitionReader 通过DefaultDocumentLoader对配置文件进行校验并转换成document,到这里配置就算是验证并加载到内存中了,下面就是通过BeanDefinitionDocumentReader解析doc标签,看下面的代码:
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//获取根节点
Element root = doc.getDocumentElement();
//从根节点开始解析标签
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
//获取beans标签的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec,
BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
//看spring.profiles.active环境变量中是否有该属性,如果没有则不加载下面的标签
if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
//标签beans可能会存在递归的情况, 每次都创建自己的解析器
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(this.readerContext, root, parent);
//解析前处理、留给子类实现
preProcessXml(root);
//解析bean definition
parseBeanDefinitions(root, this.delegate);
//解析后处理、留给子类实现
postProcessXml(root);
this.delegate = parent;
}
}
上面就是从根节点开始解析doc,这里支持标签beans嵌套的情况,这样方便我们在发布的时候改一个环境变量就可以切换多套系统配置,比如如下配置:
<beans>
<beans profile="dev">
...
</beans>
<beans profile="test">
...
</beans>
</beans>
在web中使用,在web.xml配置环境变量
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
加载配置的时序图如下:
下面就是解析beans下面的标签了,通过命名空间判断是默认标签还是自定义标签来走不同的流程。
二、默认标签解析
分四种情况import alais bean beans:
- import: 解析出resource属性获取依赖配置资源用XmlBeanDefinitionReader递归加载配置
- alais: 把别名注册到beanFactory中的aliasMap,需要判断不能循环起别名,如:a->b,b->a
- beans:递归调用解析根节点的代码doRegisterBeanDefinitions
- bean:用BeanDefinitionParserDelegate解析封装成成BeanDefinitionHolder,把解析后的BeanDefinitionHolder注册到beanFactory。到BeanDefinitionParserDelegate看BeanDefinitionHolder是怎么创建的,先创建BeanDefinition,然后把该标签下面的属性、子标签都set进BeanDefinition,最后把BeanDefinition、beanName和alias数组一起封装成BeanDefinitionHolder,看BeanDefinition的属性基本可以看出bean标签下面可以配置哪些属性。
重点看一下解析bean标签的时序图:
三、自定义标签解析
自定义标签在META-INF/spring.handlers定义的,通过标签的命名空间获取到对应的处理类handler,然后调用handler.parse方法,这里主要看命名空间跟handler的对应关系是怎么加载的。
看BeanDefinitionParserDelegate的parseCustomElement方法:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
//这个resolve方法里面很重要, namespace的Handler是在这里加载的
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中看是怎么获取的NamespaceHandler:
public NamespaceHandler resolve(String namespaceUri) {
//加载所有handlers配置
Map<String, Object> handlerMappings = getHandlerMappings();
//获取到对应handler的handler对象或者字符串
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
} else if (handlerOrClassName instanceof NamespaceHandler) {
//如果已经初始化过直接返回
return (NamespaceHandler) handlerOrClassName;
} else {
//未初始化的通过反射实例化对象,并且调用init方法,init方法是把标签跟对应的parser做映射
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);
}
}
}
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
//把properties文件加载到map中
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;
}
选一个NamespaceHandler看看它的init方法是做什么的:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the '
* {@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
//元素解析器放入parsers 集合中:
this.parsers.put(elementName, parser);
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
//这里调用BeanDefinitionParser的parse方法
return findParserForElement(element, parserContext).parse(element, parserContext);
}
//获取BeanDefinitionParser
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
//从前面加载的集合中根据标签名称获取Parser
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]",
element);
}
return parser;
}
}
到这里NamespaceHandler就获取到了,回归到上面parseCustomElement方法,handler调用parse方法,parse方法里面首先根据标签到parsers集合里面找到对应的解析器并调用对应解析器的parse方法,时序图:
来源:oschina
链接:https://my.oschina.net/u/1445156/blog/803213