Spring3.1.1浅析(一)

来源:互联网 发布:latex有windows版么 编辑:程序博客网 时间:2024/06/05 22:39

首先了解一下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;}@Overridepublic 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;}    @Overridepublic 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/beanshttp://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/beanshttp://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 hierarchy11: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.xsd11:18:46,390 DEBUG main xml.DefaultBeanDefinitionDocumentReader:108 - Loading bean definitions11: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.xsd11:18:46,421 DEBUG main xml.DefaultBeanDefinitionDocumentReader:108 - Loading bean definitions11: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 hierarchy11: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 references11: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 references11: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)) {// recursedoRegisterBeanDefinitions(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的解析。后者通过调用XmlBeanDefinitionReaderdoLoadBeanDefinitions生成要解析的Document对象,然后调用BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReaderdoRegisterBeanDefinitions方法进行真正的解析,最后把解析到的bean通过BeanDefinitionReaderUtils注册到容器中。
原创粉丝点击