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

来源:互联网 发布:名片录入软件 编辑:程序博客网 时间:2024/05/22 17:40

大体过程

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;}@Overridepublic 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 {@Overridepublic 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 所在的绝对路劲等。例如:

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

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {        ...       // Listener 的具体实现,这是 Spring 容器通过 XML 配置方式的直接入口点       // 其中 ServletContextEvent 中包含了一个 ServletContext,为Servlet 容器初始化了,传递给 Spring 供后继使用       public void contextInitialized(ServletContextEvent event) {                // 兼容低版本的 Springthis.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,创建一个新的 WebApplicationContextif (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 informationString 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.xmlString initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (initParameter != null) {wac.setConfigLocation(initParameter);}// 通过 String classNames = servletContext.getInitParameter("contextInitializerClasses")// 检查是否有 ApplicationContextInitializer<ConfigurableApplicationContext> <strong>元素</strong>,如果有则// 继续初始化customizeContext(sc, wac);// 核心逻辑:刷新 ApplicationContextwac.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

该方法调用
        // 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}
 
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对象,该对象提供一堆容器以及一些默认配置,供以后 factory 创建 bean 时装入不同中间变量和最终结果。如:
List<BeanPostProcessor>
Map<String, RootBeanDefinition>
Set<Class> ignoredDependencyInterfaces = new HashSet<Class>();
private boolean allowBeanDefinitionOverriding = true;
private final List<String> beanDefinitionNames = new ArrayList<String>();
private final Map<String, BeanDefinition> = new ConcurrentHashMap<String, BeanDefinition>(64);等。
同时,该 factory 提供ignoreDependencyInterface方法,忽略 Aware 接口的对象。该接口提供一些钩子功能,供用户反向调用 Aware 接口的具体对象。如:
ignoreDependencyInterface(.class);
ignoreDependencyInterface(.class);
ignoreDependencyInterface(.class);
以及设置 parentBeanFactory,对于全局 application 而言, parentBeanFactory 为 null。对于具体的 servlet,其 parentBeanFactory 则为 root 对应的 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());}
 最终调用方法 实现 XML 元素解析功能。
@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.                // 创建 XmlBeanDefinitionReaderXmlBeanDefinitionReader 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);                // 通过该方法<strong><strong>解析</strong></strong> XML 文件loadBeanDefinitions(beanDefinitionReader);}
首先该方法通过如下代码查找由 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[] = ((ResourcePatternResolver) resourceLoader).getResources(location);
   ----> 其值为
 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 <strong><strong>解析</strong></strong> 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 文件,<strong><strong>解析</strong></strong>其中的 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)) {// recursedoRegisterBeanDefinitions(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) {// 对应<strong><strong>解析</strong></strong>XML文件的 id, name <strong>元素</strong>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);}// <strong><strong>解析</strong></strong> <bean> <strong>元素</strong>的属性值,如 class="...", parent=...// 此处会为每个 bean 创建一个默认的 GenericBeanDefinition 对象,其有 bean 对象的一些默认值,如://  parent, beanClass, attributes, singleton = true; lazyInit = false; 等// 同时还会<strong><strong>解析</strong></strong> <bean> 里面嵌套的<strong>元素</strong>,如 meta, replaced-method, replaced-method 等// <strong><strong>解析</strong></strong>的<strong>元素</strong>还包括: 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 为空,则通过默认方式,生成一个 beanNameif (!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.// <strong><strong>解析</strong></strong> 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);// 如果不是默认命名空间,则会调用下面逻辑先<strong><strong>解析</strong></strong>命名空间://    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);// 在 resolve()里会有如下逻辑从 META-INF/spring.handlers 载入所有 handler://    Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);// 然后通过<strong>元素</strong>所属的空间名,找到对应的 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..add(beanName);
this..put(beanName, beanDefinition);
同时释放 frozenBeanDefinitionNames,mergedBeanDefinitions 等中间元素值,供后续使用。还得处理 alias元素
 
 

转载:http://www.verydemo.com/demo_c143_i3121.html

0 0
原创粉丝点击