首先了解一下spring的启动细节:
1、创建maven工程,pom文件中加入对spring的依赖和log4j的支持:
2、建立两个测试类,其中service中包含对dao的引用,然后定义一个Test类进行测试用:
3、配置文件中配置dao和service:
Log4J.properties:
log4j.rootCategory=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}%5p%t%c{2}:%L-%m%n
log4j.category.org.springframework.beans.factory=DEBUG
运行结果如下:
可见我们成功调用了ServiceFoo的doSomething方法,下面我们看一下spring在这期间究竟做了什么:
1、刷新org.springframework.context.support.ClassPathXmlApplicationContext对象,因为我们用的是ClassPathXmlApplicationContext这种方式创建ApplicationContext,所以spring启动后先调用这个类进行初始化。
那么 ClassPathXmlApplicationContext 这个类干了些什么工作呢,我们打开源码:可见,这个类就是根据location指定的位置读取xml文件,真正的读取方法是它的父类AbstractXmlApplicationContext中定义的,代码如下:
传入的参数就是DefaultListableBeanFactory,这应该是spring的默认beanFactory,然后初始化一个用来读取xml,首先设置了一下它要做的一件事就是先解析出DefaultListableBeanFactory中的资源列表,然后一个一个读取:
loadBeanDefinitions首先指定Resource和Encoding方式,并把它们放在EncodedResource对象中,接着调用读取的方法:
接下来我们断点调试一下这个方法,看看其中有什么奥秘,第一行首先读入的是class path resource [services.xml]这个配置,resourcesCurrentlyBeingLoaded这个是个ThreadLocal<Set<EncodedResource>>,也就是是个线程绑定的集合,集合中防入了EncodedResource对象,这个我们在上文中也提到过就是编码和Resource的封装对象,然后是这个集合的初始化工作,这个不用解释了,执行到这个代码时就开始读取services.xml这个文件了。
这个方法时实际解析bean定义的方法,我们看一下这个方法:
documentLoader.loadDocument方法读取了services.xml的内容并得到Document对象,当然了还有些附件操作,如格式验证,命名空间验证等。
这个方法根据刚才生成的Document对象解析bean的定义,registerBeanDefinitions除了Document之外另一个参数是ReaderContext,这个我的理解是一个和命令空间有关的XmlReaderContext对象,也就是说可能不同的命名空间下有同名的xmlReader对象,这个就是区分的作用。
说了这么多总要开始解析了吧,这个也是一个很重要的方法,其中BeanDefinitionParserDelegate对象是个bean定义的代理类,
preProcessXml(root);和postProcessXml(root);
目前是这个接口中声明的方法,没有具体实现,作用就是可以在解析xml为bean之前和之后进行一些回调操作。
parseBeanDefinitions解析终于开始了:
parseDefaultElement 进行默认的解析:定义了四种解析的方式:解析import标签、beans标签、bean标签和alias标签。其中 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
这个方法时核心方法,就是把解析到的bean注册到容器中:
beanName:得到serviceFoo
definitionHolder:保留了bean的属性信息:
Bean definition withname 'serviceFoo' and aliases []: Generic bean: class[com.yushh.test.chapter42.ServiceFoo]; scope=; abstract=false; lazyInit=false;autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false;factoryBeanName=null; factoryMethodName=null; initMethodName=null;destroyMethodName=null; defined in class path resource [services.xml]
至此完成了对一个bean的解析。
下面我们总结一下: spring 通过 ClassPathXmlApplicationContext 类的 loadBeanDefinitions 完成对 bean 的解析。后者通过调用 XmlBeanDefinitionReader 的 doLoadBeanDefinitions 生成要解析的 Document 对象,然后调用 BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader 的 doRegisterBeanDefinitions 方法进行真正的解析,最后把解析到的 bean 通过 BeanDefinitionReaderUtils 注册到容器中。