Spring3.1.1浅析(一)

首先了解一下spring的启动细节:

1、  创建maven工程,pom文件中加入对spring的依赖和log4j的支持:

2、建立两个测试类,其中service中包含对dao的引用,然后定义一个Test类进行测试用:

package com.yushh.test.chapter42;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DaoFoo {
	
	
	protected final Log log = LogFactory.getLog(DaoFoo.class);
	
	private String daoName;
	
	private String daoPartern;

	public String getDaoName() {
		return daoName;
	}

	public void setDaoName(String daoName) {
		this.daoName = daoName;
	}

	public String getDaoPartern() {
		return daoPartern;
	}

	public void setDaoPartern(String daoPartern) {
		this.daoPartern = daoPartern;
	}

	@Override
	public String toString() {
		return "DaoFoo [daoName=" + daoName + ", daoPartern=" + daoPartern
				+ "]";
	}
	
	public void doSomething(){
		log.info("DaoFoo method doSomething()!");
	}


}

package com.yushh.test.chapter42;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;


public class ServiceFoo {

	protected final Log log = LogFactory.getLog(ServiceFoo.class);
	
	private String name;
	
	private String user;
	
	private DaoFoo dao;
	
	
	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}


	public String getUser() {
		return user;
	}


	public void setUser(String user) {
		this.user = user;
	}

    
	
	@Override
	public String toString() {
		return "ServiceFoo [name=" + name + ", user=" + user + "]";
	}
	
	public void doSomething(){
		log.info("ServiceFoo's method doSomething()!");
	}


	public void setDao(DaoFoo dao) {
		this.dao = dao;
	}

	
}

package com.yushh.test.chapter42;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		ApplicationContext context =
				new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
		ServiceFoo service = context.getBean("serviceFoo", ServiceFoo.class);
		service.doSomething();
	}

}

3、  配置文件中配置dao和service:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="daoFoo" class="com.yushh.test.chapter42.DaoFoo">
</bean>
</beans>

Service.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- services -->
<bean id="serviceFoo"
	class="com.yushh.test.chapter42.ServiceFoo">
	<property name="dao" ref="daoFoo"/>
</bean>
</beans>

Log4J.properties:

log4j.rootCategory=INFO,stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}%5p%t%c{2}:%L-%m%n

log4j.category.org.springframework.beans.factory=DEBUG

运行结果如下:

11:18:46,280  INFO main support.ClassPathXmlApplicationContext:495 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@9f2a0b: startup date [Tue Mar 27 11:18:46 CST 2012]; root of context hierarchy
11:18:46,312  INFO main xml.XmlBeanDefinitionReader:315 - Loading XML bean definitions from class path resource [services.xml]
11:18:46,327 DEBUG main xml.DefaultDocumentLoader:72 - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
11:18:46,358 DEBUG main xml.PluggableSchemaResolver:140 - Loading schema mappings from [META-INF/spring.schemas]
11:18:46,358 DEBUG main xml.PluggableSchemaResolver:146 - Loaded schema mappings: {http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd, http://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd, http://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-3.1.xsd, http://www.springframework.org/schema/aop/spring-aop-3.0.xsd=org/springframework/aop/config/spring-aop-3.0.xsd, http://www.springframework.org/schema/task/spring-task-3.1.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd, http://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd, http://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd, http://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd, http://www.springframework.org/schema/jee/spring-jee-2.5.xsd=org/springframework/ejb/config/spring-jee-2.5.xsd, http://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd, http://www.springframework.org/schema/jee/spring-jee-3.1.xsd=org/springframework/ejb/config/spring-jee-3.1.xsd, http://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd, http://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd, http://www.springframework.org/schema/task/spring-task-3.0.xsd=org/springframework/scheduling/config/spring-task-3.0.xsd, http://www.springframework.org/schema/context/spring-context-2.5.xsd=org/springframework/context/config/spring-context-2.5.xsd, http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd, http://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd, http://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd, http://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-3.1.xsd, http://www.springframework.org/schema/lang/spring-lang-2.5.xsd=org/springframework/scripting/config/spring-lang-2.5.xsd, http://www.springframework.org/schema/jee/spring-jee-3.0.xsd=org/springframework/ejb/config/spring-jee-3.0.xsd, http://www.springframework.org/schema/jee/spring-jee-2.0.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd, http://www.springframework.org/schema/context/spring-context-3.1.xsd=org/springframework/context/config/spring-context-3.1.xsd, http://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd, http://www.springframework.org/schema/lang/spring-lang-3.1.xsd=org/springframework/scripting/config/spring-lang-3.1.xsd, http://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd, http://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context-3.1.xsd, http://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-3.1.xsd, http://www.springframework.org/schema/aop/spring-aop-2.5.xsd=org/springframework/aop/config/spring-aop-2.5.xsd, http://www.springframework.org/schema/aop/spring-aop-3.1.xsd=org/springframework/aop/config/spring-aop-3.1.xsd, http://www.springframework.org/schema/context/spring-context-3.0.xsd=org/springframework/context/config/spring-context-3.0.xsd, http://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd, http://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd, http://www.springframework.org/schema/lang/spring-lang-3.0.xsd=org/springframework/scripting/config/spring-lang-3.0.xsd, http://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd, http://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd, http://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd}
11:18:46,358 DEBUG main xml.PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/beans/spring-beans-3.0.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-3.0.xsd
11:18:46,390 DEBUG main xml.DefaultBeanDefinitionDocumentReader:108 - Loading bean definitions
11:18:46,405 DEBUG main xml.XmlBeanDefinitionReader:216 - Loaded 1 bean definitions from location pattern [services.xml]
11:18:46,405  INFO main xml.XmlBeanDefinitionReader:315 - Loading XML bean definitions from class path resource [daos.xml]
11:18:46,405 DEBUG main xml.DefaultDocumentLoader:72 - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
11:18:46,405 DEBUG main xml.PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/beans/spring-beans-3.0.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-3.0.xsd
11:18:46,421 DEBUG main xml.DefaultBeanDefinitionDocumentReader:108 - Loading bean definitions
11:18:46,421 DEBUG main xml.XmlBeanDefinitionReader:216 - Loaded 1 bean definitions from location pattern [daos.xml]
11:18:46,436  INFO main support.DefaultListableBeanFactory:557 - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1081d2e: defining beans [serviceFoo,daoFoo]; root of factory hierarchy
11:18:46,436 DEBUG main support.DefaultListableBeanFactory:217 - Creating shared instance of singleton bean 'serviceFoo'
11:18:46,436 DEBUG main support.DefaultListableBeanFactory:430 - Creating instance of bean 'serviceFoo'
11:18:46,436 DEBUG main support.DefaultListableBeanFactory:504 - Eagerly caching bean 'serviceFoo' to allow for resolving potential circular references
11:18:46,452 DEBUG main support.DefaultListableBeanFactory:217 - Creating shared instance of singleton bean 'daoFoo'
11:18:46,452 DEBUG main support.DefaultListableBeanFactory:430 - Creating instance of bean 'daoFoo'
11:18:46,452 DEBUG main support.DefaultListableBeanFactory:504 - Eagerly caching bean 'daoFoo' to allow for resolving potential circular references
11:18:46,452 DEBUG main support.DefaultListableBeanFactory:458 - Finished creating instance of bean 'daoFoo'
11:18:46,468 DEBUG main support.DefaultListableBeanFactory:458 - Finished creating instance of bean 'serviceFoo'
11:18:46,468 DEBUG main support.DefaultListableBeanFactory:245 - Returning cached instance of singleton bean 'daoFoo'
11:18:46,483 DEBUG main support.DefaultListableBeanFactory:245 - Returning cached instance of singleton bean 'lifecycleProcessor'
11:18:46,483 DEBUG main support.DefaultListableBeanFactory:245 - Returning cached instance of singleton bean 'serviceFoo'
11:18:46,483  INFO main chapter42.ServiceFoo:46 - ServiceFoo's method doSomething()!

可见我们成功调用了ServiceFoodoSomething方法,下面我们看一下spring在这期间究竟做了什么:

1、刷新org.springframework.context.support.ClassPathXmlApplicationContext对象,因为我们用的是ClassPathXmlApplicationContext这种方式创建ApplicationContext,所以spring启动后先调用这个类进行初始化。

那么 ClassPathXmlApplicationContext 这个类干了些什么工作呢,我们打开源码:
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
		this(configLocations, true, null);
	}

可见,这个类就是根据location指定的位置读取xml文件,真正的读取方法是它的父类AbstractXmlApplicationContext中定义的,代码如下:

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(this.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);
		loadBeanDefinitions(beanDefinitionReader);
	} 

传入的参数就是DefaultListableBeanFactory,这应该是spring的默认beanFactory,然后初始化一个

XmlBeanDefinitionReader
用来读取 xml ,首先设置了一下
XmlBeanDefinitionReader
它要做的一件事就是先解析出 DefaultListableBeanFactory 中的资源列表,然后一个一个读取:


public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}

loadBeanDefinitions首先指定ResourceEncoding方式,并把它们放在EncodedResource对象中,接着调用读取的方法:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				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();
			}
		}
	}

接下来我们断点调试一下这个方法,看看其中有什么奥秘,第一行首先读入的是class path resource [services.xml]这个配置,resourcesCurrentlyBeingLoaded这个是个ThreadLocal<Set<EncodedResource>>,也就是是个线程绑定的集合,集合中防入了EncodedResource对象,这个我们在上文中也提到过就是编码和Resource的封装对象,然后是这个集合的初始化工作,这个不用解释了,

InputStream inputStream = encodedResource.getResource().getInputStream();
执行到这个代码时就开始读取services.xml这个文件了。

doLoadBeanDefinitions
这个方法时实际解析 bean 定义的方法,我们看一下这个方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}
		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);
		}
	}

documentLoader.loadDocument方法读取了services.xml的内容并得到Document对象,当然了还有些附件操作,如格式验证,命名空间验证等。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

这个方法根据刚才生成的Document对象解析bean的定义,registerBeanDefinitions除了Document之外另一个参数是ReaderContext,这个我的理解是一个和命令空间有关的XmlReaderContext对象,也就是说可能不同的命名空间下有同名的xmlReader对象,这个就是区分的作用。

说了这么多总要开始解析了吧,
protected void doRegisterBeanDefinitions(Element root) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			Assert.state(this.environment != null, "environment property must not be null");
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!this.environment.acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		// any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createHelper(readerContext, root, parent);

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

这个也是一个很重要的方法,其中BeanDefinitionParserDelegate对象是个bean定义的代理类,

preProcessXml(root);postProcessXml(root);

目前是这个接口中声明的方法,没有具体实现,作用就是可以在解析xmlbean之前和之后进行一些回调操作。

parseBeanDefinitions解析终于开始了:

parseDefaultElement 进行默认的解析:

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);
		}
	}

定义了四种解析的方式:解析import标签、beans标签、bean标签和alias标签。其中          BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());

这个方法时核心方法,就是把解析到的bean注册到容器中:

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String aliase : aliases) {
				registry.registerAlias(beanName, aliase);
			}
		}
	}

beanName:得到serviceFoo

definitionHolder:保留了bean的属性信息:

Bean definition withname 'serviceFoo' and aliases []: Generic bean: class[com.yushh.test.chapter42.ServiceFoo]; scope=; abstract=false; lazyInit=false;autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false;factoryBeanName=null; factoryMethodName=null; initMethodName=null;destroyMethodName=null; defined in class path resource [services.xml]

至此完成了对一个bean的解析。

 

下面我们总结一下: spring 通过 ClassPathXmlApplicationContext 类的 loadBeanDefinitions 完成对 bean 的解析。后者通过调用 XmlBeanDefinitionReader doLoadBeanDefinitions 生成要解析的 Document 对象,然后调用 BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader doRegisterBeanDefinitions 方法进行真正的解析,最后把解析到的 bean 通过 BeanDefinitionReaderUtils 注册到容器中。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值