Spring 3.2 源码解析 -- XML bean 元素到 BeanDefinition 解析过程

大体过程:

XML 的 <bean ...> ---> Document. element ---> GenericBeanDefinition ---> AbstractBeanDefinition implements BeanDefinition ---> BeanDefinitionHolder ----> registory<bean, beanDefinition>

一、 观察者模式

        Java 使用观察者模式可以通过下面两个java.util.EventListener 和 java.util.EventoObject 的类和接口实现。

    1. Create listeners

package com.colorcc.test.event.listener;

import java.util.EventListener;
import java.util.EventObject;

public interface ColorccListener<E extends EventObject> extends EventListener {
	
	public void onAction(E e);

}
package com.colorcc.test.event.listener;

import com.colorcc.test.event.object.StartEvent;

public class StartListener implements ColorccListener<StartEvent> {
	
	private String name;
	
	public StartListener (String name) {
		this.name = name;
	}

	@Override
	public void onAction(StartEvent event) {
		System.out.println(name + " ---- start listener ..." + event.getName());
	}

}

     2. Create event

package com.colorcc.test.event.object;

import java.util.EventObject;

public class StartEvent extends EventObject {

	private static final long serialVersionUID = 7312681426368400653L;
	
	public StartEvent(Object source) {
		super(source);
	}
	
	public String getName() {
		return " start event ";
	}
}

3. Publish the event

package com.colorcc.test.event.publish;

import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;

import com.colorcc.test.event.listener.StartListener;
import com.colorcc.test.event.object.StartEvent;

public class StartEventPublisher implements EventPublisher {

	@Override
	public void publishEvent(EventObject event) {
		if (event instanceof StartEvent) {
			StartEvent startEvent = (StartEvent) event;

			for (StartListener startListener : getStartListener(startEvent)) {
				startListener.onAction(startEvent);
			}
		}
	}

	public static List<StartListener> getStartListener(StartEvent event) {
		List<StartListener> linsteners = new ArrayList<StartListener>();
		linsteners.add(new StartListener("linstener1"));
		linsteners.add(new StartListener("linstener2"));
		linsteners.add(new StartListener("linstener3"));
		return linsteners;
	}

	public static void main(String[] args) {
		// 创建一个活动
		StartEvent event = new StartEvent("start");
		// 得到该活动对应所有监听器
		for (StartListener startListener : getStartListener(event)) {
			// 触发监听器的监听动作
			startListener.onAction(event);
		}

	}
}

Servlet 的 观察者例子如下:

public interface ServletContextListener extends EventListener {
    // 通过 web.xml 配置,在 Servlet 容器启动时,调用该接口
    public void contextInitialized(ServletContextEvent sce);

    public void contextDestroyed(ServletContextEvent sce);
}
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        ...
       // Listener 的具体实现,这是 Spring 容器通过 XML 配置方式的直接入口点
       // 其中 ServletContextEvent 中包含了一个 ServletContext,为Servlet 容易其境初始化了,传递给 Spring 供后继使用
       public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}
        ....
}
public class ServletContextEvent extends java.util.EventObject { 

    private static final long serialVersionUID = -7501701636134222423L;

    public ServletContextEvent(ServletContext source) {
        super(source);
    }

    public ServletContext getServletContext () { 
        return (ServletContext) super.getSource();
    }
}
public class JettyWebAppContext extends WebAppContext extentd ... implements ServletContext

二、Spring 容器启动的入口点

Spring 容器启动的一种方式为配置 web.xml,加入如下行

<listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Spring 容器启动的原理如下:

    2.1.  Servlet (Jetty)容器启动,会读取 web project 的 WEB-INF/web.xml 文件,并使用 web.xml 的相关配置,初始化 ServletContext (JettyWebContext),该 context 包括 web 自身的一些信息。如 Servlet 的版本号,用户 web project 所在的绝对路劲等。例如:

[email protected]{/,file:/opt/dev/workspace_jack/register-api/src/main/webapp/},file:/opt/dev/workspace_jack/register-api/src/main/webapp/

   然后当读到 <listener> 元素时,调用 Spring 容器的:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        ...
       // Listener 的具体实现,这是 Spring 容器通过 XML 配置方式的直接入口点
       // 其中 ServletContextEvent 中包含了一个 ServletContext,为Servlet 容器初始化了,传递给 Spring 供后继使用
       public void contextInitialized(ServletContextEvent event) {
                // 兼容低版本的 Spring
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
                // 此处为 Spring 容易初始化的起始位置
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}
        ....
}

   注: ContextLoadListener 在加载的时候,会通过其父类的 <cinit> 方法,读取 properties 配置文件,将 XmlWebApplicationContext 作为容器初始化的默认方式。

2.2 其中 initWebApplicationContext 内容如下:

/**
	 * Initialize Spring's web application context for the given servlet context,
	 * using the application context provided at construction time, or creating a new one
	 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
	 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
	 * @param servletContext current servlet context
	 * @return the new WebApplicationContext
	 * @see #ContextLoader(WebApplicationContext)
	 * @see #CONTEXT_CLASS_PARAM
	 * @see #CONFIG_LOCATION_PARAM
	 */
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 如果 context 已经存在,则抛出异常
		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!");
		}

		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
// 刚启动,则 content (WebApplicationContext) 为 null,创建一个新的 WebApplicationContext
			if (this.context == null) {
// 该处读取 web.xml 的 配置文件 contextClass 属性
//       String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
// 如果存在,则用该属性的 class 初始化容器,否则使用默认 (XmlWebApplicationContext) 初始化容器,
// 最终通过反射方式返回一个 ConfigurableWebApplicationContext (WebApplicationContext)对象

// <?xml version="1.0" encoding="UTF-8"?>
// <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
// 	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
// 	version="3.0">
// 	<display-name>User Register</display-name>
// 	<context-param>
// 		<param-name>webAppRootKey</param-name>
// 		<param-value>register.root</param-value>
// 	</context-param>
//      <context-param>
// 		<param-name>contextClass</param-name>
// 		<param-value>com.colorcc.test.ContextClass</param-value>
// 	</context-param>
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
// 该方法为 Spring 容易启动的逻辑所在
				configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
			}
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}
 Spring 容器启动的业务逻辑
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				// Generate default id...
				if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
					// Servlet <= 2.4: resort to name specified in web.xml, if any.
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.getServletContextName()));
				}
				else {
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.getContextPath()));
				}
			}
		}

		// Determine parent for root web application context, if any.
		ApplicationContext parent = loadParentContext(sc);

		wac.setParent(parent);
		wac.setServletContext(sc);
// 配置 Spring 容器初始化的 配置文件位置,以后程序会设置默认为 applicationContext.xml
		String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (initParameter != null) {
			wac.setConfigLocation(initParameter);
		}
// 通过 String classNames = servletContext.getInitParameter("contextInitializerClasses")
// 检查是否有 ApplicationContextInitializer<ConfigurableApplicationContext> 元素,如果有则
// 继续初始化
		customizeContext(sc, wac);

// 核心逻辑:刷新 ApplicationContext
		wac.refresh();
	}
 Spring 核心
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}
		}
	}
  三、 Spring refresh 3.1 prepareRefresh();   收集 systemProperties, systemEnv, ServletContext 等 Spring 容器的值,以便启动或者刷新 Spring 容器使用。
该方法调用         // Initialize any placeholder property sources in the context environment
        initPropertySources(); 首先创建 StandardServletEnvironment 对象,该对象实现 ConfigurableWebEnvironment 接口。初始化如下一些属性[servletConfigInitParams,servletContextInitParams,jndiProperties,systemProperties,systemEnvironment] 作为 environment 的  propertySources (MutablePropertySources)属性。
/** Servlet context init parameters property source name: {@value} */
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

/** Servlet config init parameters property source name: {@value} */
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

/** JNDI property source name: {@value} */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

/** System environment property source name: {@value} */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
 其中 servletContextInitParams 的值为 servlet 初始化的 servletContext 值。 servletConfigInitParams 的值为 servlet 初始化的 servletConfig 值 null. systemProperties = System.getProperties() 值:
{java.runtime.name=Java(TM) SE Runtime Environment, sun.boot.library.path=/opt/dev/jdk/jre/lib/amd64, java.vm.version=20.6-b01, java.vm.vendor=Sun Microsystems Inc., java.vendor.url=http://java.sun.com/, guice.disable.misplaced.annotation.check=true, path.separator=:, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, file.encoding.pkg=sun.io, sun.java.launcher=SUN_STANDARD, user.country=US, sun.os.patch.level=unknown, java.vm.specification.name=Java Virtual Machine Specification, user.dir=/opt/dev/workspace_jack/register-api, java.runtime.version=1.6.0_31-b04, java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment, java.endorsed.dirs=/opt/dev/jdk/jre/lib/endorsed, os.arch=amd64, java.io.tmpdir=/tmp, line.separator=
, java.vm.specification.vendor=Sun Microsystems Inc., os.name=Linux, classworlds.conf=/opt/dev/maven/bin/m2.conf, sun.jnu.encoding=UTF-8, java.library.path=/opt/dev/jdk/jre/lib/amd64/server:/opt/dev/jdk/jre/lib/amd64:/opt/dev/jdk/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib, java.specification.name=Java Platform API Specification, java.class.version=50.0, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, os.version=3.6.10-2.fc16.x86_64, user.home=/home/qtj, user.timezone=PRC, java.awt.printerjob=sun.print.PSPrinterJob, file.encoding=UTF-8, java.specification.version=1.6, java.class.path=/opt/dev/maven/boot/plexus-classworlds-2.4.jar, user.name=qtj, java.vm.specification.version=1.0, sun.java.command=org.codehaus.plexus.classworlds.launcher.Launcher jetty:run, java.home=/opt/dev/jdk/jre, sun.arch.data.model=64, user.language=en, java.specification.vendor=Sun Microsystems Inc., java.vm.info=mixed mode, java.version=1.6.0_31, securerandom.source=file:/dev/./urandom, java.ext.dirs=/opt/dev/jdk/jre/lib/ext:/usr/java/packages/lib/ext, sun.boot.class.path=/opt/dev/jdk/jre/lib/resources.jar:/opt/dev/jdk/jre/lib/rt.jar:/opt/dev/jdk/jre/lib/sunrsasign.jar:/opt/dev/jdk/jre/lib/jsse.jar:/opt/dev/jdk/jre/lib/jce.jar:/opt/dev/jdk/jre/lib/charsets.jar:/opt/dev/jdk/jre/lib/modules/jdk.boot.jar:/opt/dev/jdk/jre/classes, java.vendor=Sun Microsystems Inc., maven.home=/opt/dev/maven, file.separator=/, java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport.cgi, sun.io.unicode.encoding=UnicodeLittle, sun.cpu.endian=little, sun.desktop=gnome, sun.cpu.isalist=}
 systemEnvironment = System.getenv() 值:
{TERM=dumb, M3_HOME=/opt/dev/maven, JAVA_HOME=/opt/dev/jdk, IMSETTINGS_MODULE=IBus, KDEDIRS=/usr, SESSION_MANAGER=local/unix:@/tmp/.ICE-unix/1654,unix/unix:/tmp/.ICE-unix/1654, GNOME_DESKTOP_SESSION_ID=this-is-deprecated, IMSETTINGS_INTEGRATE_DESKTOP=yes, MAIL=/var/spool/mail/qtj, MYSQL_HOME=/usr/local/mysql, GDMSESSION=gnome, XDG_SESSION_COOKIE=ffe1f31cb920b0b7dc5fbf9c00000010-1359204139.589544-488163775, PWD=/opt/dev/workspace_jack/register-api, HOSTNAME=tianjie, CVS_RSH=ssh, NLSPATH=/usr/dt/lib/nls/msg/%L/%N.cat, HISTSIZE=1000, PATH=/opt/dev/jdk/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/mysql/bin:/opt/dev/maven/bin:/opt/dev/jvmstat/bin:/opt/dev/mongodb/bin:/home/qtj/.local/bin:/home/qtj/bin, KDE_IS_PRELINKED=1, XAUTHORITY=/var/run/gdm/auth-for-qtj-JeQMx2/database, GDM_LANG=en_US.utf8, XDG_SEAT=seat0, WINDOWPATH=1, XDG_SESSION_ID=2, USERNAME=qtj, XDG_VTNR=1, GNOME_KEYRING_CONTROL=/tmp/keyring-RzXvpG, SHLVL=1, XFILESEARCHPATH=/usr/dt/app-defaults/%L/Dt, JVMSTAT_HOME=/opt/dev/jvmstat, MONGODB_HOME=/opt/dev/mongodb, QT_IM_MODULE=xim, LOGNAME=qtj, GPG_AGENT_INFO=/tmp/keyring-RzXvpG/gpg:0:1, XMODIFIERS=@im=ibus, SSH_AUTH_SOCK=/tmp/keyring-RzXvpG/ssh, LD_LIBRARY_PATH=/opt/dev/jdk/jre/lib/amd64/server:/opt/dev/jdk/jre/lib/amd64:/opt/dev/jdk/jre/../lib/amd64, OLDPWD=/opt/dev/workspace_jack/register-api, DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-k39Bi7yZfr,guid=0c1549cdbe6af23acd2ba61400000040, SHELL=/bin/bash, GNOME_KEYRING_PID=1648, MAVEN_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8083,server=y,suspend=y, CLASSPATH=.:/opt/dev/jdk/lib/dt.jar:/opt/dev/jdk/lib/tools.jar, DESKTOP_SESSION=gnome, DISPLAY=:0.0, USER=qtj, HOME=/home/qtj, HISTCONTROL=ignoredups, LESSOPEN=||/usr/bin/lesspipe.sh %s, XDG_RUNTIME_DIR=/run/user/qtj, LANG=en_US.utf8}
  3.2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 读取 Xml 配置文件,解析其中元素,创建 BeanDefinition 对象。
protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
  该方法调用 DefaultListableBeanFactory beanFactory = createBeanFactory(); 初始化一个 DefaultListableBeanFactory对象,该对象 提供一堆容器以及一些默认配置,供以后 factory 创建 bean 时装入不同中间变量和最终结果。如: List<BeanPostProcessor> beanPostProcessors Map<String, RootBeanDefinition> mergedBeanDefinitions Set<Class> ignoredDependencyInterfaces = new HashSet<Class>(); private boolean allowBeanDefinitionOverriding = true; private final List<String> beanDefinitionNames = new ArrayList<String>(); private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);等。 同时,该 factory 提供 ignoreDependencyInterface方法,忽略 Aware 接口的对象。该接口提供一些钩子功能,供用户反向调用 Aware 接口的具体对象。如: ignoreDependencyInterface( BeanNameAware.class);
ignoreDependencyInterface( BeanFactoryAware.class);
ignoreDependencyInterface( BeanClassLoaderAware.class); 以及 设置 parentBeanFactory,对于全局 application 而言, parentBeanFactory 为 null。对于具体的 servlet,其 parentBeanFactory 则为 root 对应的 beanFactory。 该方法还调用 customizeBeanFactory(beanFactory); 方法: 实现如下功能:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
		beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
	}
 最终调用方法 loadBeanDefinitions(beanFactory); 实现 XML 元素的解析功能。
@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
                // 创建 XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
                
                // Configure the bean definition reader with this context's
		// resource loading environment.
                // 设置 reader 的一些默认值
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
                // 注意该处设置一个 schema 的默认值
                // public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
                // this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
                // 即读取每个 jar 包的 META-INF/spring.schemas 文件,知道对应的 schema 文件
		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);

                // 通过该方法解析 XML 文件
		loadBeanDefinitions(beanDefinitionReader);
	}
分析 loadBeanDefinitions(XmlBeanDefinitionReader reader) 方法 首先该方法通过如下代码查找由 web.xml 中配置的 Spring 全局XML 配置文件文件。 String[] configLocations = getConfigLocations(); 由下代码便可知我们 project 中默认的 applicationContext.xml 的由来。 这也是 Spring 的一个常用规则: 到处设置默认值,便于用户使用。
/** Default config location for the root context */
	public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

	/** Default prefix for building a config location for a namespace */
	public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

	/** Default suffix for building a config location for a namespace */
	public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";



protected String[] getDefaultConfigLocations() {
		if (getNamespace() != null) {
			return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
		}
		else {
			return new String[] {DEFAULT_CONFIG_LOCATION};
		}
	}
 最终调用的一些业务逻辑代码如下: reader.loadBeanDefinitions(configLocation); return loadBeanDefinitions(location, null); Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);    ----> 其值为 [ServletContext resource [/WEB-INF/applicationContext.xml]]  int loadCount = loadBeanDefinitions(resources);   XmlBeanDefinitionReader :
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
 doLoadBeanDefinitions(inputSource, encodedResource.getResource());
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
// 通过调用 JAXP 解析 applicationContext.xml 文件为 Document 对象
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}
                ...
}
 根据 Document 对象,解析其中每个元素:
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

// private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
// BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
// 该处设置了 spring.handlers 的默认值
// public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION="META-INF/spring.handlers";
// this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
// 即读取每个 jar 包中的 META-INF/spring.handlers 文件,解析其中的 property 值
// 最终创建的 XmlReaderContext 为:
// XmlReaderContext(Resource resource, ProblemReporter problemReporter,
//       ReaderEventListener eventListener, SourceExtractor sourceExtractor, 
//       XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver)
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
 其中 registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 的调用逻辑为: doRegisterBeanDefinitions(Element root) -->  BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createHelper(readerContext, root, parent);
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
// 创建 BeanDefinitionParserDelegate 对象
		BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);
// 使用父 delegate 和默认值,初始化 delegate 对应的属性值
		delegate.initDefaults(root, parentDelegate);
		return delegate;
	}
通过 BeanDefinitionParserDelegate 代码,我们看到一个XML一个 bean 元素包含的属性如下:
public class BeanDefinitionParserDelegate {

	public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

	public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";

	/** @deprecated as of Spring 3.1 in favor of {@link #MULTI_VALUE_ATTRIBUTE_DELIMITERS} */
	public static final String BEAN_NAME_DELIMITERS = MULTI_VALUE_ATTRIBUTE_DELIMITERS;

	/**
	 * Value of a T/F attribute that represents true.
	 * Anything else represents false. Case seNsItive.
	 */
	public static final String TRUE_VALUE = "true";

	public static final String FALSE_VALUE = "false";

	public static final String DEFAULT_VALUE = "default";

	public static final String DESCRIPTION_ELEMENT = "description";

	public static final String AUTOWIRE_NO_VALUE = "no";

	public static final String AUTOWIRE_BY_NAME_VALUE = "byName";

	public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";

	public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";

	public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";

	public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";

	public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";

	public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";

	public static final String NAME_ATTRIBUTE = "name";

	public static final String BEAN_ELEMENT = "bean";

	public static final String META_ELEMENT = "meta";

	public static final String ID_ATTRIBUTE = "id";

	public static final String PARENT_ATTRIBUTE = "parent";

	public static final String CLASS_ATTRIBUTE = "class";

	public static final String ABSTRACT_ATTRIBUTE = "abstract";

	public static final String SCOPE_ATTRIBUTE = "scope";

	public static final String SINGLETON_ATTRIBUTE = "singleton";

	public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";

	public static final String AUTOWIRE_ATTRIBUTE = "autowire";

	public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";

	public static final String PRIMARY_ATTRIBUTE = "primary";

	public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";

	public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";

	public static final String INIT_METHOD_ATTRIBUTE = "init-method";

	public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";

	public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";

	public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";

	public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";

	public static final String INDEX_ATTRIBUTE = "index";

	public static final String TYPE_ATTRIBUTE = "type";

	public static final String VALUE_TYPE_ATTRIBUTE = "value-type";

	public static final String KEY_TYPE_ATTRIBUTE = "key-type";

	public static final String PROPERTY_ELEMENT = "property";

	public static final String REF_ATTRIBUTE = "ref";

	public static final String VALUE_ATTRIBUTE = "value";

	public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";

	public static final String REPLACED_METHOD_ELEMENT = "replaced-method";

	public static final String REPLACER_ATTRIBUTE = "replacer";

	public static final String ARG_TYPE_ELEMENT = "arg-type";

	public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";

	public static final String REF_ELEMENT = "ref";

	public static final String IDREF_ELEMENT = "idref";

	public static final String BEAN_REF_ATTRIBUTE = "bean";

	public static final String LOCAL_REF_ATTRIBUTE = "local";

	public static final String PARENT_REF_ATTRIBUTE = "parent";

	public static final String VALUE_ELEMENT = "value";

	public static final String NULL_ELEMENT = "null";

	public static final String ARRAY_ELEMENT = "array";

	public static final String LIST_ELEMENT = "list";

	public static final String SET_ELEMENT = "set";

	public static final String MAP_ELEMENT = "map";

	public static final String ENTRY_ELEMENT = "entry";

	public static final String KEY_ELEMENT = "key";

	public static final String KEY_ATTRIBUTE = "key";

	public static final String KEY_REF_ATTRIBUTE = "key-ref";

	public static final String VALUE_REF_ATTRIBUTE = "value-ref";

	public static final String PROPS_ELEMENT = "props";

	public static final String PROP_ELEMENT = "prop";

	public static final String MERGE_ATTRIBUTE = "merge";

	public static final String QUALIFIER_ELEMENT = "qualifier";

	public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";

	public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";

	public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";

	public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";

	public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";

	public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";

	public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";

	public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";

        private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
 同时,通过DocumentDefaultsDefinition 我们可以看到 beans 的全局配置属性如下:
public class DocumentDefaultsDefinition implements DefaultsDefinition {

	private String lazyInit;

	private String merge;

	private String autowire;

	private String dependencyCheck;

	private String autowireCandidates;

	private String initMethod;

	private String destroyMethod;

	private Object source;
  现在逻辑为通过 parseBeanDefinitions(root, this.delegate); 解析 bean 元素:其代码如下:
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);
		}
	}
 其中 DefaultNamespace 为 beans 的默认元素,用 parseDefaultElement(ele, delegate);解析 DefaultNamespace为beans的非默认元素(如 context, mvc等),用delegate.parseCustomElement(ele);解析 DefaultNamespace 不为 beans,则通过 delegate.parseCustomElement(root); 解析 我们先看第一种请客:
	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 的代码:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
    // 将生成的 beanDefinition 存入 registry 中
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); --> parseBeanDefinitionElement(ele, null);
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 对应解析XML文件的 id, name 元素
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		if (containingBean == null) {
// name 需唯一
			checkNameUniqueness(beanName, aliases, ele);
		}
// 解析 <bean> 元素的属性值,如 class="...", parent=...
// 此处会为每个 bean 创建一个默认的 GenericBeanDefinition 对象,其有 bean 对象的一些默认值,如:
//  parent, beanClass, attributes, singleton = true; lazyInit = false; 等
// 同时还会解析 <bean> 里面嵌套的元素,如 meta, replaced-method, replaced-method 等
// 解析的元素还包括: constructor-arg, property, qualifier 等。 最终一个例子如:

// Generic bean: class [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in ServletContext resource [/WEB-INF/applicationContext.xml]
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
// 如果 beanName 为空,则通过默认方式,生成一个 beanName
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);
						// Register an alias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected for Spring 1.2/2.0 backwards compatibility.
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
// 最终将 beanDefinition, beanName, aliasesArray 赋值给 BeanDefinitionHolder 并返回
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}
 代码 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 逻辑如下:
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
			Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {

		BeanDefinitionHolder finalDefinition = definitionHolder;

		// Decorate based on custom attributes first.
// 解析 bean 对应的 attribute 属性,如:
// [autowire="default", autowire-candidate="default", class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer", id="propertyConfigurer", lazy-init="default", p:location="WEB-INF/jdbc.properties"]
		NamedNodeMap attributes = ele.getAttributes();
		for (int i = 0; i < attributes.getLength(); i++) {
			Node node = attributes.item(i);
// 如果不是默认命名空间,则会调用下面逻辑先解析命名空间:
//    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 在 resolve()里会有如下逻辑从 META-INF/spring.handlers 载入所有 handler:
//    Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
// 然后通过元素所属的空间名,找到对应的 NamespaceHandler, 如: SimplePropertyNamespaceHandler,MvcNamespacehandler
//    通过反射方式方式,实例化对应的 handler,并调用 init() 方法,实现初始化。
// 最终通过 handler 的 decorate 方法转换 XML 对应的 attribute 值为 Bean 对应的 MutablePropertyValues 值,并赋值给 definition.
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}

		// Decorate based on custom nested elements.
		NodeList children = ele.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
			}
		}
		return finalDefinition;
	}
 这样通过如上的操作,得到 bean 元素对应的 BeanDefinition,验证一定的逻辑之后,将其存入 beanFacotry 相关容器中,供后面程序使用。 this. beanDefinitionNames.add(beanName); this. beanDefinitionMap.put(beanName, beanDefinition); 同时释放 frozenBeanDefinitionNames,mergedBeanDefinitions 等中间元素值,供后续使用。还得处理 alias 元素。 至此,一个 bean 解析为一个 BeanDefinition,以后 Spring 容器会使用该 definition 实例化一个 bean 对象。    

猜你喜欢

转载自tianjieqin-126-com.iteye.com/blog/1776066