一、从容器初始化分析
1.实例代码
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml));
2.XmlBeanFactory初始化时序图
3.XmlBeanFactory的构造方法
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);//reader-->XmlBeanDefinitionReader
}
可以看到加载bean的逻辑都交给了XmlBeanDefinitionReader的loadBeanDefinitions方法。
4.加载Bean的时序图
为什么用EncodedResource包装:允许指定用于解析文件的编码
4.1doLoadBeanDefinitions
doLoadBeanDefinitions方法中部分核心逻辑代码
Document doc = doLoadDocument(inputSource, resource);//加载xml文件并得到对应的Document
return registerBeanDefinitions(doc, resource);
4.2解析及注册BeanDefinitions
之前是XML加载解析的准备阶段,doRegisterBeanDefinitions(root)方法开始真正的解析
(类DefaultBeanDefinitionDocumentReader)
1.先处理profile属性,通常在配置文件中部署两套配置来适用于生产环境和开发环境,通过指定profile属性来 切换配置环境。
2.获取到用于解析XML bean定义的委托类BeanDefinitionParserDelegate
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
3.开始解析
preProcessXml(root);//解析前处理,留给子类扩展实现
parseBeanDefinitions(root, this.delegate);//进行解析
postProcessXml(root);//解析后处理,留给子类扩展实现
4.3判断是默认标签还是自定义标签分别采用不用解析逻辑
方法parseBeanDefinitions(root,delegate)解析文档中根级别的元素
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方法,如果是自定义标签则调用代理类的parseCustomElement方法。判断标砖为命名空间是否为http://www.springframework.org/schema/beans。
5.默认标签的解析
//parseDefaultElement方法内部逻辑
//对于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);
}
//对于beans标签的处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
5.1bean标签的解析及注册
1.1首先委托delegate类进行元素解析;
1.2返回BeanDefinitionHolder实例,此时bdHolder已经包含了配置文件中配置的各种属性例如class、name、id、alias之类的属性;
1.3当返回bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析;
1.4返回装饰后的bdHolder;
1.5委托BeanDefinitionUtils向Register容器中注册
1.6最后发出响应事件,通知相关监听器。
5.2用于属性承载的BeanDefinition
BeanDefinition是配置文件中<bean元素标签在容器中的内部表现形式,属性也是一一对应,然后spring将这些BeanDefinition注册到BeanDefinitionRegistry中,就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从该Registry中读取配置信息。
6.自定义标签的解析
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));
}
1.获取自定义标签的名称空间
2.根据命名空间找到对应的NamespaceHandler
3.根据标签名找到handler中对应解析器调用parse方法
类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;
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
}
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
}
}
return this.handlerMappings;
}
1.getHandlerMappings方法将读取路径下所有META-INF/spring.handlers文件,里面有关于handler的映射关系。
2.在resolve方法中,根据标签的命名空间获取到对应的handler信息,可能是类名也可能是实例,如果是实例就直接返回,如果是类名则先实例化handler,并且调用init方法向该handler注册对应的标签解析器,并且更新映射关系,类名替换为实例。
3.获取到handler后调用parse方法,找到对应的标签解析器并调用解析器的parse方法。
举例:spring-context
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
1.在spring-context包下有META-INF/spring.handlers文件如上
2.在spring的xml配置文件中引入如下标签 对应名称空间:http://www.springframework.org/schema/context
<context:component-scan base-package="com.fjeport"
annotation-config="true">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
3.解析自定义标签时候根据名称空间映射找到了对应的ContextNamespaceHandler类
//ContextNamespaceHandler的init方法
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());
}
4.可以看到init方法中注册了好多个解析器,由于我们这里用的context:component-scan标签,所以选择ComponentScanBeanDefinitionParser这个解析器来调用parse方法。
5.ComponentScanBeanDefinitionParser逻辑:扫描指定包下的所有class文件,如果该class符合条件则解析出对应BeanDefition并向容器注册,判断条件excludeFilters,includeFilters。