Spring源码浅析——spring容器的启动流程与spring IOC的实现

简介

  • Spring是一个轻量级的AOP和IOC框架。主要用来简化Java应用程序的开发,降低组件和组件之间的耦合性。大约有20个模块,主要由七个模块组成,分别是Spring Core、Spring Context、Spring AOP、Spring Dao、Spring ORM、Spring Web和Spring MVC。
  • 其中,Core和Beans模块是框架的基础部分,提供IOC和依赖注入。
  • Spring Ioc就是对java反射及读取xml文件的使用的封装
  • 本文将简要介绍和分析Spring项目启动的流程,以及启动过程中ApplicationContext如何实现IOC控制反转。

SSM开发模式中容器启动流程

  • web项目中容器启动的流程,起点是web.xml中配置的ContextLoaderListener监听器

调用流程图

在这里插入图片描述

流程解析

  • Tomcat服务器启动时会读取项目中web.xml中的配置项来生成ServletContext,ServletContext表示的是一整个应用,其中囊括应用的所有内容,ContextLoaderListener是ServletContextListener接口的实现类,它会时刻监听ServletContext的动作,包括创建和销毁
    ServletContext创建的时候会触发其contextInitialized()初始化方法的执行。

ContextLoaderListener

package org.springframework.web.context;

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
	public ContextLoaderListener() {
	}

	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	//初始化根web容器的方法。其中调用了initWebApplicationContext方法进行Spring web容器的具体创建
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

ContextLoader

  • 在这段源码中主要是概述Spring容器的创建和初始化,分别由两个方法实现:createWebApplicationContext方法和configureAndRefreshWebApplicationContext方法。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		//SpringIOC容器的重复性创建校验
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		//记录Spring容器创建开始时间
		long startTime = System.currentTimeMillis();

		try {
			if (this.context == null) {
				//创建Spring容器实例
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					//容器只有被刷新至少一次之后才是处于active(激活)状态
					if (cwac.getParent() == null) {
						//此处是一个空方法,返回null,也就是不设置父级容器
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//重点操作:配置并刷新容器   从这里开始进入IOC
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			//将创建完整的Spring容器作为一条属性添加到Servlet容器中
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				//如果当前线程的类加载器是ContextLoader类的类加载器的话,也就是说
				// 如果是当前线程加载了ContextLoader类的话,
				// 则将Spring容器在ContextLoader实例中保留一份引用
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}
  • 创建Spring容器实例 this.context = createWebApplicationContext(servletContext);
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	//决定使用哪个容器实现,在web.xml中以 <context-param> 的形式设置contextclass参数来指定使用哪个容器实现类
	// 若未指定则使用默认的XmlWebApplicationContext,这个默认的容器实现配置Spring源码web模块resource资源下
	//ContextLoader.properties文件中
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	//反射方式创建容器实例
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
  • 重点操作:配置并刷新容器 从这里开始进入IOC。configureAndRefreshWebApplicationContext(cwac, servletContext);
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
		if (idParam != null) {
			wac.setId(idParam);
		}
		else {
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}
	//在当前Spring容器中保留对Servlet容器的引用
	wac.setServletContext(sc);
	//设置web.xml中配置的contextConfigLocation参数值到当前容器中
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
	if (configLocationParam != null) {
		wac.setConfigLocation(configLocationParam);
	}
	//在容器刷新之前,提前进行属性资源的初始化,以备使用,将ServletContext设置为servletContextInitParams
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
	}
	//取得web.xml中配置的contextInitializerClasses和globalInitializerClasses对应的初始化器,
	// 并执行初始化操作,需自定义初始化器
	customizeContext(sc, wac);
	//刷新容器  从这里进入IOC
	wac.refresh();
}

Spring IOC实现

  • 查看wac.refresh()的使用,调用的是AbstractApplicationContext类下的refresh方法实现
package org.springframework.context.support;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	...... 省略无数代码
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			/**
			 * 1.准备刷新的上下文环境
			 * 在某些情况下项目需要读取某些系统变量,这个变量可能会影响系统的正确性
			 */
			prepareRefresh();
			/**
			 * 2.初始化BeanFactory,并进行XML文件读取
			 * 这一步骤中会复用BeanFactory中的配置文件读取解析以及其他功能
			 * 这一步之后,ClassPathXmlApplicationContext已经包含BeanFactory所提供的功能
			 * 可以进行Bean的提取等基础操作
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
			/**
			 * 3.对BeanFactory进行各种功能填充
			 * 增加对@qualifier与@Autowired两个注释的支持
			 */
			prepareBeanFactory(beanFactory);

			try {
				/**
				 * 4.子类覆盖方法做额外的处理
				 * postProcessBeanFactory是一个空函数,符合Spring的开放式设计
				 * 方便程序员在业务上做进一步扩展
				 */
				postProcessBeanFactory(beanFactory);

				/**
				 * 5.激活各种BeanFactory处理器
				 */
				invokeBeanFactoryPostProcessors(beanFactory);

				/**
				 * 6.注册拦截Bean创建的Bean处理器,这里只是注册,真正调用是在getBean时
				 */
				registerBeanPostProcessors(beanFactory);

				/**
				 * 7.为上下文初始化Message源,即对不同语言的消息体进行国际化处理
				 */
				initMessageSource();

				/**
				 * 8.初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
				 */
				initApplicationEventMulticaster();

				/**
				 * 9.留给子类初始化其他的Bean
				 */
				onRefresh();

				/**
				 * 10.在所有注册的bean中查找Listener bean,注册到消息广播器
				 */
				registerListeners();

				/**
				 * 11.初始化剩下的单例实例(并非懒加载)
				 */
				finishBeanFactoryInitialization(beanFactory);

				/**
				 * 12.完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程
				 * 并发出contextRefreshEvent通知
				 */
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
}

上述步骤二:obtainFreshBeanFactory()

  • 初始化BeanFactory,并进行XML文件读取。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	/**
	 * 初始化BeanFactory,并进行XML文件读取,
	 * 并将得到的BeanFactory记录在当前实体属性中
	 * refreshBeanFactory是核心
	 */
	refreshBeanFactory();
	return getBeanFactory();
}

refreshBeanFactory()刷新BeanFactory

  • 此处refreshBeanFactory()获取的是AbstractApplicationContext子类AbstractRefreshableApplicationContext的refreshBeanFactory方法。
@Override
protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		/**
		 * 1.创建DefaultListableBeanFactory
		 */
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		/**
		 * 2.为了序列化指定id,如果需要的话,让beanFactory从
		 * id反序列化到BeanFactory对象
		 */
		beanFactory.setSerializationId(getId());
		/**
		 * 3.定制beanFactory,设置相关属性,包括是否允许覆盖
		 * 同名称的不同定义的对象以及循环依赖以及
		 * 设置@Autowired和@Qualifier注解解析器QualifierAnnotationAutowire
		 */
		customizeBeanFactory(beanFactory);
		/**
		 * 4.初始化DocumentReader,并进行XML文件读取及解析
		 */
		loadBeanDefinitions(beanFactory);
		/**
		 * 5.使用全局变量记录BeanFactory类实例
		 */
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

查看上述步骤四:进行XML文件读取及解析

  • 此处loadBeanDefinitions(beanFactory)获取的是AbstractRefreshableApplicationContext子类AbstractRefreshableConfigApplicationContext的子类AbstractRefreshableWebApplicationContext的子类XmlWebApplicationContext的loadBeanDefinitions方法
package org.springframework.web.context.support;
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		
		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
		
		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		// 加载并解析Bean的定义
		loadBeanDefinitions(beanDefinitionReader);
	}
}

loadBeanDefinitions(beanDefinitionReader)

  • 此处loadBeanDefinitions(beanDefinitionReader)在当前类中调用的是AbstractBeanDefinitionReaderr类的loadBeanDefinitions方法,而AbstractBeanDefinitionReaderr调用的是BeanDefinitionReader的实现类AbstractBeanDefinitionReader下的实现类XmlBeanDefinitionReader的loadBeanDefinitions方法
package org.windframework.beans.factory.xml;
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	// 3.2.从指定的XML配置文件读取Bean
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        /**
         * 3.3.
         * 对参数resource使用EncoderResource进行封装
         * 推测主要是用于对资源文件的编码进行处理。
         * 主要逻辑体现在getReader()方法中,当设置了编码属性的时候
         * Spring会使用相应的编码作为输入流的编码
         */
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    // 3.3.从指定的XML配置文件读取Bean  真正的数据准备阶段
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        /**
         * 通过属性来记录已经加载的资源
         */
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            /**
             * 获取输入流,从resource获取对应的InputStream并构造InputSource
             */
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                // 考虑到Resource可能存在编码要求的情况,其次,通过SAX读取XML文件的方式来准备InputSource对象
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                /**
                 * 通过构造的InputSource和Resource实例调用doLoadBeanDefinitions
                 * 3.4.真正进入逻辑核心部分 从配置文件加载Bean
                 */
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                inputStream.close();
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        } finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

	// 3.4.从配置文件加载Bean
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            /**
             * 不考虑异常类代码,此方法就做了3件事
             * 1.获取对XML文件的验证模式(DTD 或 XSD)
             * 2.加载XML文件,并得到对应的Document
             */
            // 3.4.1.使用配置的Document读取指定的文档
            Document doc = doLoadDocument(inputSource, resource);
            /**
             * 3.4.2.
             * 把XML文件转换为Document后,接下来提取以及注册bean就是我们的重头戏
             * 根据返回的Document注册Bean信息
             */
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        } catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        } catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        } catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

	// 3.4.2.注册Bean
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        /**
         * 这个方法很好地体现了【单一职责原则】
         * 实例化的工作在createBeanDefinitionDocumentReader完成
         */
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        /**
         * 记录统计前BeanDefinition的加载个数
         */
        int countBefore = getRegistry().getBeanDefinitionCount();
        /**
         * 3.4.2.1.记载及注册bean,经过各种调用,我们终于到达核心逻辑的底部,真正开始进行解析了
         */
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        /**
         * 记录本次加载的BeanDefinition个数
         */
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
}

核心逻辑 registerBeanDefinitions(doc, createReaderContext(resource))

  • registerBeanDefinitions(doc, createReaderContext(resource))调用的是实现类DefaultBeanDefinitionDocumentReader的 registerBeanDefinitions方法
package org.windframework.beans.factory.xml;

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

	// 根据XSD或DTD文件解析Bean
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

    // 3.4.2.1.根据给定的根元素解析Bean 解析Bean的核心正式开始
    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        /**
         * BeanDefinitionParserDelegate专门处理Bean解析
         */
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            /**
             * 处理profile属性
             * profile的官方实例代码
             * <beans xmlns="http:..../schema/beans"
             *          xmlns:xsi="http:..../XMLSchema-instance"
             *          xmlns:jdbc="http:..../schema/jdbc"
             *          xmlns:jee="http:..../schema/jee"
             *          xsi:schemaLocation="">
             *    <beans profile="dev">
             *      ....
             *    </beans>
             *    <beans profile="prod">
             *      ....
             *    </beans>
             * </beans>
             * 集成到Web环境中时,在web.xml加入以下代码
             * <context-param>
             *     <param-name>Spring.profiles.active</param-name>
             *     <param-value>dev</param-value>
             * </context-param>
             * 有了这个特性,我们可以在配置文件中部署两套配置来适用于生产环境和开发环境
             *
             * 首先程序会获取beans节点是否定义了profile属性,如果定义了则会到环境变量中寻找
             */
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        /**
         * 解析前的处理,留给子类实现
         */
        preProcessXml(root);
        /**
         * 3.4.2.2.处理了profile后就可以进行XML的读取了
         */
        parseBeanDefinitions(root, this.delegate);
        /**
         * 解析后处理,留给子类实现
         */
        postProcessXml(root);
        /**
         * 为什么preProcessXml(root)或postProcessXml(root)都是空的?
         * 一个类要么是面向继承设计的,要么就用final修饰。这俩个方式是为子类设计的
         * 使用的是设计模式【模板方法模式】,如果继承自DefaultBeanDefinitionDocumentReader
         * 的子类需要在Bean解析前后做一些处理,重写这两个方法即可
         */
        this.delegate = parent;
    }
}

进行XML的读取

package org.windframework.beans.factory.xml;

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

	// 3.4.2.2.解析根XML文档的元素,例如import\alias\bean
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        /**
         * 对beans的处理,判断是否是默认的命名空间,因为Spring的XML配置里有两大Bean声明
         * 默认:<bean id="test" class="test.TestBean"/>
         * 自定义:<tx:annotation-driven/>
         */
        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)) {
                        /**
                         * 对bean的处理(根节点或子节点默认命名空间时)
                         */
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        /**
                         * 对bean的处理(自定义命名空间时)
                         */
                        /**
                         * 自定义标签使用
                         * 很多情况下,需要为系统提供可配置化支持
                         * 由于配置比较复杂,Spring提供Schema的支持,大致需要如下几步(代码 简化)
                         * 1.创建一个需要扩展的组件
                         * class User {
                         *     private String userName;
                         *     private String email;
                         *     //省略get set 方法
                         * }
                         * 2.定义一个XSD文件描述组件内容
                         * <xml version="1.0" encoding="UTF-8">
                         *     // 定义一个新的targetNamespace,并在空间中定义了一个
                         *     // name为user的element,user有3个属性。
                         *     // XSD文件是XML DTD的代替者
                         * <schema xmlns="http://www.w3.org/2001/XMLSchema"
                         * targetNamespace="http://www.lexueba.com/schema/user"
                         * xmlns:tns="http://www.lexueba.com/schema/user"
                         * elementFormDefault="qualified">
                         * <element name="user">
                         * 		<complexType>
                         * 		 	<attribute name="id" type="string"/>
                         * 		 	<attribute name="userName" type="string"/>
                         * 		 	<attribute name="email" type="string"/>
                         * 		</complexType>
                         * </element>
                         * 3.创建一个文件,实现BeanDefinitionParser接口解析XSD文件中的定义和组件定义
                         * class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
                         *     // Element对应的类
                         *     protected Class getBeanClass(Element element) {
                         *         return User.class;
                         *     }
                         *     // 从element中解析并提取对应的元素
                         *     protected void doParse(Element element, BeanDefinitionBuilder bean) {
                         *         String userName = element.getAttribute("userName");
                         *         String email = element.getAttribute("email");
                         *         // 将提取的数据放入BeanDefinitionBuilder中,
                         *         // 待到bean全部完成解析后统一注册到beanFactory中
                         *         if (StringUtils.hasText(userName)) {
                         *             bean.addPropertyValue("userName", username);
                         *         }
                         *         if (StringUtils.hasText(userName)) {
                         * 						               bean.addPropertyValue("email", email);
                         * 						           }
                         *     }
                         * }
                         * 4.创建一个Handler文件,扩展自NamespaceHandlerSupport将组件注册到Spring容器
                         * class MyNamespaceHandler extends NamespaceHandlerSupport {
                         *     void init() {
                         *     		// 当遇到自定义标签<user:aaa>类似以user开头的元素,就会进行解析
                         *         registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
                         *     }
                         * }
                         * 5.编写Spring.handlers和Spring.schemas文件(默认在/META-INF文件夹下)
                         *
                         */
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

    // 3.4.2.2.对bean的处理(根节点或子节点默认命名空间时)
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        /**
         * 针对不同标签【import、alias、bean、beans】做不同处理
         * 其中针对bean标签的解析最复杂也最重要
         */
        /**
         * import解析步骤如下:
         * 1. 获取resource属性所表示的路径
         * 2. 解析路径中的系统属性,格式如${user.dir}
         * 3. 判定location时绝对路径还是相对路径
         * 4. 如果绝对路径则递归调用bean的解析过程,进行另一次的解析
         * 5. 如果是相对路径则计算出绝对路径并进行解析
         * 6. 通知监听器, 解析完成
         */
        // 解析import标签
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // 解析alias标签
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // 3.4.2.2.1. 解析bean标签  对bean标签的解析最复杂也最重要,因此从此开始分析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // 解析beans标签
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }
}

对bean标签的解析最复杂也最重要,因此从此开始分析

package org.windframework.beans.factory.xml;

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
	// 3.4.2.2.1.解析bean标签
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        /**
         * 1)首先委托BeanDefinitionParserDelegate 的 parseBeanDefinitionElement 方法进行元素解析
         * 返回的bdHolder包含我们配置文件中配置的各种属性
         * 例如class name id alias之类的属性
         */
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            /**
             * 2)对解析后的bdHolder进行注册 若返回的bdHolder不为空且存在默认的子节点还有自定义属性
             * 需要再次对自定义进行解析、
             */
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance
                /**
                 * 3)解析完成后,需要对解析后的bdHolder进行注册
                 */
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            /**
             * 4)发出响应事件,通知相关的监听器,这个bean已经加载完成
             */
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值