(转)菜鸟分析Spring源代码之 IOC容器的启动
20111225 By Tony
1. 前言
6月份开始,我就打算做一个Android下的快速开发架构。详见以下文章:
http://blog.youkuaiyun.com/nanjingjiangbiao/article/details/6557150
其中,我曾设想引入Spring来做整体的基础架构。后来发展到,我想研究研究Spring的底层代码。
只可惜项目太忙,只好先花钱买了本计先生的《Spring技术内幕》一书.
好书啊,好书啊,只可惜我看得头晕晕。从头到尾到处是代码,中文理解写的高深莫测,只可惜一个UML图都没有,真是天书啊天书。
怎么办呢?上网看评论,都说看不懂这本书的人都不是好程序员。
没办法,只好自我分析,自我批判Spring源代码。
2. 从Spring的最简单的Sample说起
上网到处都能抄到,以下这种小sample
- package com.hyron.tony;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.xml.XmlBeanFactory;
- import org.springframework.core.io.ClassPathResource;
- public class Test {
- public static void main(String[] args)throws Exception {
- BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
- GreetingService greetingService = (GreetingService)factory.getBean("greetingService");
- greetingService.sayGreeting();
- }
- }
以上最起码有两个东西我想搞清,
第一, XML文件的解析规则和算法在那里
第二, Object的动态生成在哪里
1. 分析
先上sample的时序图
首先,简单的紧,applicationContext.xml被构造成ClassPathResource对象,代码如下
- public ClassPathResource(String path, ClassLoader classLoader) {
- Assert.notNull(path, "Path must not be null");
- String pathToUse = StringUtils.cleanPath(path);
- if (pathToUse.startsWith("/")) {
- pathToUse = pathToUse.substring(1);
- }
- this.path = pathToUse;
- this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
- }
上面完成两个事情,第一是对文件path的编排。第二是,生成一个classloader出来,具体如下:
- public static ClassLoader getDefaultClassLoader() {
- ClassLoader cl = null;
- try {
- cl = Thread.currentThread().getContextClassLoader();
- }
- catch (Throwable ex) {
- // Cannot access thread context ClassLoader - falling back to system class loader...
- }
- if (cl == null) {
- // No thread context class loader -> use class loader of this class.
- cl = ClassUtils.class.getClassLoader();
- }
- return cl;
- }
但是,但是。。。。每一个Resource对象都有一个类加载器的变量??
接下来,资源的加载,也就是XML文件的解析,我们在XmlBeanFactory中看到如下代码:
- private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
- public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
- super(parentBeanFactory);
- this.reader.loadBeanDefinitions(resource);
- }
委托啊,委托啊,有木有啊有木有。。
XmlBeanFactory聚合了一个XmlBeanDefinitionReader来处理解析的事情。
然后,我们去XmlBeanDefinitionReader看看XML的解析
代码片段:
- //一个HashSet的ThreadLocal变量,用来保证在多线程情况下的数据安全
- //每一个进来的Resource都会被包装进去
- Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
然后,开始读XML文件咯
代码片段:
- //读stream
- InputStream inputStream = encodedResource.getResource().getInputStream();
- //inputstream被包装成JDK的document
- Document doc = this.documentLoader.loadDocument(
- inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
- //拿到document的根元素,开始登记
- Element root = doc.getDocumentElement();
- doRegisterBeanDefinitions(root);
下面就所谓的document的登记,这块的代码还是比较有水平的。
代码片段:
- //循环Dom树,开始解析
- 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);
- }
- }
- }
- //分别为import,alias,bean,beans四种元素做解析
- private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
- if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
- importBeanDefinitionResource(ele);
- }
- else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
- processAliasRegistration(ele);
- }
- else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
- processBeanDefinition(ele, delegate);
- }
- else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
- // recurse
- doRegisterBeanDefinitions(ele);
- }
- }
- //已bean元素为例,真正的解析代码如下
- //将DOM节点的属性都塞入AbstractBeanDefinition对象中
- public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
- BeanDefinition containingBean, AbstractBeanDefinition bd) {
- if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
- // Spring 2.x "scope" attribute
- bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
- if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
- error("Specify either 'scope' or 'singleton', not both", ele);
- }
- }
- else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
- // Spring 1.x "singleton" attribute
- bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
- BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
- }
- else if (containingBean != null) {
- // Take default from containing bean in case of an inner bean definition.
- bd.setScope(containingBean.getScope());
- }
- if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
- bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
- }
- String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
- if (DEFAULT_VALUE.equals(lazyInit)) {
- lazyInit = this.defaults.getLazyInit();
- }
- bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
- String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
- bd.setAutowireMode(getAutowireMode(autowire));
- String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
- bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
- if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
- String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
- bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
- }
- String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
- if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
- String candidatePattern = this.defaults.getAutowireCandidates();
- if (candidatePattern != null) {
- String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
- bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
- }
- }
- else {
- bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
- }
- if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
- bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
- }
- if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
- String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
- if (!"".equals(initMethodName)) {
- bd.setInitMethodName(initMethodName);
- }
- }
- else {
- if (this.defaults.getInitMethod() != null) {
- bd.setInitMethodName(this.defaults.getInitMethod());
- bd.setEnforceInitMethod(false);
- }
- }
- if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
- String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
- if (!"".equals(destroyMethodName)) {
- bd.setDestroyMethodName(destroyMethodName);
- }
- }
- else {
- if (this.defaults.getDestroyMethod() != null) {
- bd.setDestroyMethodName(this.defaults.getDestroyMethod());
- bd.setEnforceDestroyMethod(false);
- }
- }
- if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
- bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
- }
- if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
- bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
- }
- return bd;
- }
- //最后,在bean容器中注册BeanDefiniton
- //这里所谓的bean容器就是XmlBeanFactory在构造的时候,塞入的DefaultListableBeanFactory
- //其中,储存用的数据结构是个线程安全的ConcurrentHashMap
- synchronized (this.beanDefinitionMap) {
- Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
- if (oldBeanDefinition != null) {
- if (!this.allowBeanDefinitionOverriding) {
- throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
- "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
- "': There is already [" + oldBeanDefinition + "] bound.");
- }
- else {
- if (this.logger.isInfoEnabled()) {
- this.logger.info("Overriding bean definition for bean '" + beanName +
- "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
- }
- }
- }
- else {
- this.beanDefinitionNames.add(beanName);
- this.frozenBeanDefinitionNames = null;
- }
- this.beanDefinitionMap.put(beanName, beanDefinition);
- resetBeanDefinition(beanName);
- }
。。。至于Bean的生成,下次再说吧,累了,去吃饭去了。。。
Bean的生成,中间有关于单例,线程安全,性能,缓存,都会是比较有意思的问题。