spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录。xml配置文件的解析过程
先把测试的代码贴出来吧
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "> 5 6 <bean id="userBean" class="com.luban.springsource.xml.UserBean"></bean> 7 </beans> 8 9 10 11 public class XmlBeanTest { 12 public static void main(String[] args) { 13 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 14 System.out.println(ac.getBean(UserBean.class)); 15 } 16 }
UserBean就是一个普通的类,没有加任何注解
下面我们来一步一步解析源码
对于Xml格式的,是通过ClassPathXmlApplicationContext来实现的,首先会进入到对应的构造函数中
1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { 2 super(parent); 3 this.setConfigLocations(configLocations); 4 if (refresh) { 5 this.refresh(); 6 } 7 }
在这段代码中,第三行这里,主要是把当前要解析的xml文件存放到了一个String数组中,在后面解析的时候,会遍历这个数据,挨个解析xml文件
this.refresh()方法完成了解析和初始化,主要来看这个方法
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 //准备工作包括设置启动时间、是否激活标志位、初始化属性源配置 6 prepareRefresh(); 7 8 // Tell the subclass to refresh the internal bean factory. 9 /** 10 * 返回一个factory 11 * xml格式的配置文件,是在这个方法中扫描到beanDefinitionMap中的 12 * 在org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)中会创建一个XmlBeanDefinitionReader来解析xml文件 13 * 会把bean.xml解析成一个InputStream,然后再解析成document格式 14 * 按照document格式解析,从root节点进行解析,判断root节点是bean?还是beans?还是import等,如果是bean 15 * 就把解析到的信息包装成beanDefinitionHolder,然后调用DefaultListablebeanFactory的注册方法将bean放到beanDefinitionMap中 16 */ 17 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 18 19 // Prepare the bean factory for use in this context. 20 //准备工厂 21 prepareBeanFactory(beanFactory); 22 23 try { 24 // Allows post-processing of the bean factory in context subclasses. 25 postProcessBeanFactory(beanFactory); 26 27 // Invoke factory processors registered as beans in the context. 28 invokeBeanFactoryPostProcessors(beanFactory); 29 30 // Register bean processors that intercept bean creation. 31 registerBeanPostProcessors(beanFactory); 32 33 // Initialize message source for this context. 34 //初始化MessageSource组件(该组件在spring中用来做国际化、消息绑定、消息解析) 35 initMessageSource(); 36 37 // Initialize event multicaster for this context. 38 /** 39 * 注册一个多事件派发器 40 * 先从beanFactory获取,如果没有,就创建一个,并将创建的派发器放到beanFactory中 41 */ 42 initApplicationEventMulticaster(); 43 44 // Initialize other special beans in specific context subclasses. 45 /** 46 * 这是一个空方法,在springboot中,如果集成了Tomcat,会在这里new Tomcat() 47 */ 48 onRefresh(); 49 50 // Check for listener beans and register them. 51 /** 52 * 注册所有的事件监听器 53 * 将容器中的时间监听器添加到 applicationEventMulticaster 中 54 * 55 */ 56 registerListeners(); 57 58 // Instantiate all remaining (non-lazy-init) singletons. 59 /** 60 * TODO 61 * 完成对bean的实例化 62 * 63 * 主要的功能都在这里面 64 */ 65 finishBeanFactoryInitialization(beanFactory); 66 67 // Last step: publish corresponding event. 68 /** 69 * 当容器刷新完成之后,发送容器刷新完成事件 70 * publishEvent(new ContextRefreshedEvent(this)); 71 */ 72 finishRefresh(); 73 } 74 75 catch (BeansException ex) { 76 if (logger.isWarnEnabled()) { 77 logger.warn("Exception encountered during context initialization - " + 78 "cancelling refresh attempt: " + ex); 79 } 80 81 // Destroy already created singletons to avoid dangling resources. 82 destroyBeans(); 83 84 // Reset 'active' flag. 85 cancelRefresh(ex); 86 87 // Propagate exception to caller. 88 throw ex; 89 } 90 91 finally { 92 // Reset common introspection caches in Spring's core, since we 93 // might not ever need metadata for singleton beans anymore... 94 resetCommonCaches(); 95 } 96 } 97 }
在前面的spring源码博客中有说到过,spring初始化的一个大致流程是这样的:
1.把所有的要注入的class解析,存到beanDefinitionMap中
2.循环遍历beanDefinitionMap,把每个beanDefinition经过初始化,实例化等生命周期方法的处理,转变为spring容器中存储的bean
3.把bean存到bean的单实例池中
今天要说的,主要是第一步这里,把class解析到beanDefinition中,对于注解版的配置,是在invokeBeanFactoryPostProcessors(beanFactory);中完成,区分了@ComponentScan、ImportSeletor、ImportBeanDefinitionRegistrar、@Bean这么几种情况
那么,xml是另外一种方式,主要是在ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();中,完成了对bean.xml的解析
这个方法,进来之后,首先是刷新beanFactory(),其中,有一个loadBeanDefinitions(beanFactory);方法
这个方法里面的逻辑还是比较简单的,大致说一下:
1.new一个xmlBeanDefinitionReader
2.遍历前面存放的配置文件的数组,依次解析
3.将xml配置文件转换成inputSource,然后生成一个Document对象; Document doc = this.doLoadDocument(inputSource, resource); 这里的这个方法,就是把当前xml配置文件转解析成一个document对象,对于这个方法,
我没有怎么深入研究,姑且我们就认为这是一个黑盒方法
真正开始解析xml,是从这个方法中开始的
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); int countBefore = this.getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource)); return this.getRegistry().getBeanDefinitionCount() - countBefore; }
从这里的registerBeanDefinitions方法开始来解析的
这里是解析root根节点(beans)之后,会获取到当前root下面的子节点,然后依次循环处理子节点的代码;
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, "import")) { 3 this.importBeanDefinitionResource(ele); 4 } else if (delegate.nodeNameEquals(ele, "alias")) { 5 this.processAliasRegistration(ele); 6 } else if (delegate.nodeNameEquals(ele, "bean")) { 7 this.processBeanDefinition(ele, delegate); 8 } else if (delegate.nodeNameEquals(ele, "beans")) { 9 this.doRegisterBeanDefinitions(ele); 10 } 11 12 }
从上面说的registerBeanDefinitions到这里的解析方法中间没有几步,不细说了,直接把调用链给出来,可以自己debug再细看一下
我们来说解析的这个方法:
会依次判断beans子节点是哪几个,由于我们只配置了一个<bean,所以,来看处理bean的方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
在处理<bean>的时候,会返回一个BeanDefinitionHolder对象,在解析注解版的class的时候,也有好多地方是这样用的,把class解析成BeanDefinitionHolder,然后再把beanDefinitionHolder对应的BeanName,作为BeanDefinitionMap的key,
holder里面的beanDefinition作为value;这里来着重看parseBeanDefinitionElement()方法,因为经过这个方法之后,已经解析成beanDefinition了
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
//获取xml中bean节点配置的class(就是类的全类名)
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//根据全类名,通过反射
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析<bean>节点的属性信息,比如:lazy/autowire/scope/destroy-method/init-method等,然后把解析到的属性set到beanDefinition中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//解析bean的子节点 <description>的信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
//下面两个方法分别解析<bean>的子节点 lookup-method和replaced-method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析bean的构造函数,最后调用的是bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
这里面,加了一些注释,不细看每个方法了,其实就是解析每个配置的信息,然后把属性信息set到beanDefinition中
总结
1.首先把入参中待解析的xml文件存到了一个string数组中
2.依次遍历数组,来解析
3.解析的时候,会把xml文件解析成document对象,然后首先从根节点<beans>开始解析
4.解析到<bean>节点的时候,会依次解析bean的属性信息以及bean的子节点信息,把解析到的属性set到beanDefinition中
5.把beanDefinition再put到beanDefinitionMap中