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 注册到容器中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值