spring Ioc源码解读-xml资源加载与解析
2017-11-30 21:03
761 查看
Spring对内部使用到的资源比如spring.xml实现了自己的抽象结构,Spring利用Resource接口封装底层资源文件
Resource接口继承了InputStreamSource接口
这个接口封装任何能够返回InputStream的类,它只有一个方法getInputStream()该方法返回一个新的InputStream对象,Resource接口对不同来源的资源文件都有相应的Resource实现
FileSystemResource
ClasspathResource
UrlResource
InputStreamResource
ByteArrayResource
有时候我们需要读取classpath路径下的一个文件进行操作,我们可以使用spring提供的ClassPathResource类读取文件
当Resource相关实现类完成了对资源的封装后,我们可以将配置文件的读取工作交给XmlBeanDefinitionReader来处理
XmlBeanFactory调用如下这个构造函数进行初始化
XmlBeanFactory依赖关系如下所示
在XmlBeanFactory构造方法中调用了父类DefaultListableBeanFactory的构造方法,而后DefaultListableBeanFactory又调用了AbstractWutowireCapableBeanFactory的构造方法
ignoreDependencyInterface方法的功能是忽略给定接口的自动装配功能.比如说,当A中有属性B的时候,那么当Spring在获取A的Bean的时候,如果其属性B还没有初始化,那么Spring会自动初始化B,但是如果B实现了BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,spring会忽略对其自动装配,而是通过其他途径对其进行装配
XmlBeanFactory调用完super()父类构造方法后,继续调用this.reader.loadBeanDefinitions(resource);方法加载资源
加载资源是会调用如下的一个方法,
这个方法非常的冗长,核心代码try包围的三行,三行代码主要的目的如下
1. 获取对xml文件的验证模式
2. 加载xml文件,并得到相应的Document
3. 根据返回的Document注册bean信息
第三项是注册bean的重点
BeanDefinitionDocumentReader是一个接口用于bean的注册
在本例中调用的是BeanDefinitionDocumentReader的实现之一DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法如下
首先获取了root节点用于以便再次将root作为参数继续BeanDefinition的注册,而delegate用于解析xml中定义的各种属性
比如
随后parseBeanDefinitions(root, delegate);开始解析bean的定义了
spring中标签有两种,一类是默认的标签,如
另一类是自定义标签如
那么spring如何确认标签是自定义的还是默认的标签呢?spring依赖固定的命名空间http://www.springframework.org/schema/beans进行对比,如果一致就是默认否则就是自定义.对这两种标签的解析采用了不同的方式,将在接下来的文章中讲解
public interface Resource extends InputStreamSource { boolean exists(); boolean isReadable(); boolean isOpen(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); }
Resource接口继承了InputStreamSource接口
public interface InputStreamSource { InputStream getInputStream() throws IOException; }
这个接口封装任何能够返回InputStream的类,它只有一个方法getInputStream()该方法返回一个新的InputStream对象,Resource接口对不同来源的资源文件都有相应的Resource实现
FileSystemResource
ClasspathResource
UrlResource
InputStreamResource
ByteArrayResource
有时候我们需要读取classpath路径下的一个文件进行操作,我们可以使用spring提供的ClassPathResource类读取文件
Resource resource = new ClassPathResource("spring.xml"); InputStream in = resource.getInputStream();
当Resource相关实现类完成了对资源的封装后,我们可以将配置文件的读取工作交给XmlBeanDefinitionReader来处理
XmlBeanFactory调用如下这个构造函数进行初始化
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
XmlBeanFactory依赖关系如下所示
在XmlBeanFactory构造方法中调用了父类DefaultListableBeanFactory的构造方法,而后DefaultListableBeanFactory又调用了AbstractWutowireCapableBeanFactory的构造方法
public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
ignoreDependencyInterface方法的功能是忽略给定接口的自动装配功能.比如说,当A中有属性B的时候,那么当Spring在获取A的Bean的时候,如果其属性B还没有初始化,那么Spring会自动初始化B,但是如果B实现了BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,spring会忽略对其自动装配,而是通过其他途径对其进行装配
XmlBeanFactory调用完super()父类构造方法后,继续调用this.reader.loadBeanDefinitions(resource);方法加载资源
加载资源是会调用如下的一个方法,
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); } }
这个方法非常的冗长,核心代码try包围的三行,三行代码主要的目的如下
1. 获取对xml文件的验证模式
2. 加载xml文件,并得到相应的Document
3. 根据返回的Document注册bean信息
第三项是注册bean的重点
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //beanMap中已有的bean个数 int countBefore = getRegistry().getBeanDefinitionCount(); //注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //返回本次注册bean的个数 return getRegistry().getBeanDefinitionCount() - countBefore; }
BeanDefinitionDocumentReader是一个接口用于bean的注册
在本例中调用的是BeanDefinitionDocumentReader的实现之一DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法如下
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); preProcessXml(root); parseBeanDefinitions(root, delegate); postProcessXml(root); }
首先获取了root节点用于以便再次将root作为参数继续BeanDefinition的注册,而delegate用于解析xml中定义的各种属性
比如
public static final String SCOPE_ATTRIBUTE = "scope"; public static final String SINGLETON_ATTRIBUTE = "singleton"; public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
随后parseBeanDefinitions(root, delegate);开始解析bean的定义了
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(delegate.getNamespaceURI(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; String namespaceUri = delegate.getNamespaceURI(ele); if (delegate.isDefaultNamespace(namespaceUri)) { //解析默认标签 parseDefaultElement(ele, delegate); } else { //解析自定义标签 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
spring中标签有两种,一类是默认的标签,如
<bean id="test" class="com.spring.aop.test1.TestBean"/>
另一类是自定义标签如
<aop:aspectj-autoproxy/>
那么spring如何确认标签是自定义的还是默认的标签呢?spring依赖固定的命名空间http://www.springframework.org/schema/beans进行对比,如果一致就是默认否则就是自定义.对这两种标签的解析采用了不同的方式,将在接下来的文章中讲解
相关文章推荐
- Spring源码解读-Spring IoC容器初始化之资源解析
- Spring源码解读-Spring IoC容器初始化之资源定位
- Spring源码阅读-- IOC容器资源解析
- Spring Ioc 源码分析(一)- XML 解析
- spring源码解读-加载解析配置文件
- Spring源码分析--Ioc容器定位解析资源文件并注册BeanDefinition
- Spring IOC 源码阅读资源加载和注册
- spring IOC源码之解析xml中各个元素的过程
- [Spring3.x源码]IoC(二)解析XML建立上下文
- spring、mybatis加载xml源码解析
- Spring IOC 源码阅读之资源定位加载
- Spring源码解析:资源的描述与加载
- spring IOC源码学习(二):BeanDefinition资源加载
- Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean
- Spring源码解读2——Bean资源的载入/解析
- Spring源码解读之Xml解析Annotation解析
- 源码跟读,Spring是如何解析和加载xml中配置的beans
- Spring IoC源码解析(一)——配置文件加载和IoC容器初始化
- Spring源码解读-Spring IoC容器初始化之资源注册
- 通过DefaultListableBeanFactory加载.xml配置文件学习Spring-IoC容器注册/加载bean的机制(源码走读)