曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享

曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解

曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean definition的?

曹工说Spring Boot源码(5)-- 怎么从properties文件读取bean

工程代码地址 思维导图地址

工程结构图:

曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

整体流程

这次,我们打算讲一下,spring启动时,是怎么去读取xml文件的,bean的解析部分可能暂时涉及不到,因为放到一起,内容就太多了,具体再看吧。

给ClassPathXmlApplicationContext设置xml文件的路径

refresh内部的beanFactory,其实这时候BeanFactory都还没创建,会先创DefaultListableBeanFactory

ClassPathXmlApplicationContext调用其loadBeanDefinitions,将新建DefaultListableBeanFactory作为参数传入

ClassPathXmlApplicationContext内部会持有一个XmlBeanDefinitionReader,且XmlBeanDefinitionReader内部是持有之前创建的DefaultListableBeanFactory的,这时候就简单了,XmlBeanDefinitionReader负责读取xml,将bean definition 解析出来,丢给DefaultListableBeanFactory,此时,XmlBeanDefinitionReader就算完成了,退出历史舞台

上面第四步完成后,DefaultListableBeanFactory里面其实一个业务bean都没有,只有一堆的 bean definition,后面ClassPathXmlApplicationContext直接去实例化那些需要在启动过程中实例化的bean。

当前,这中间还涉及到使用beanDefinitionPostProcessor和beanPostProcessor对beanFactory进行处理,这都是后话了。

这次,我们主要讲的部分是,第4步。

入口代码 位置:org.springframework.context.support.AbstractRefreshableApplicationContext @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建一个DefaultListableBeanFactory 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); } }

上面调用了

AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { // 创建一个从xml读取beanDefinition的读取器 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 配置环境 beanDefinitionReader.setEnvironment(this.getEnvironment()); // 配置资源loader,一般就是classpathx下去获取xml beanDefinitionReader.setResourceLoader(this); // xml解析的解析器 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); // 核心方法,使用beanDefinitionReader去解析xml,并将解析后的bean definition放到beanFactory loadBeanDefinitions(beanDefinitionReader); } xml中xsd、dtd解析器 // xml解析的解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

这个类实现的接口就是jdk里面的org.xml.sax.EntityResolver,这个接口,只有一个方法,主要负责xml里,外部实体的解析:

public interface EntityResolver { public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException; }

大家可能不太明白,我们看看怎么实现的吧:

public class DelegatingEntityResolver implements EntityResolver { /** Suffix for DTD files */ public static final String DTD_SUFFIX = ".dtd"; /** Suffix for schema definition files */ public static final String XSD_SUFFIX = ".xsd"; private final EntityResolver dtdResolver; private final EntityResolver schemaResolver; public DelegatingEntityResolver(ClassLoader classLoader) { this.dtdResolver = new BeansDtdResolver(); this.schemaResolver = new PluggableSchemaResolver(classLoader); } // 主要看这里,感觉就是对我们xml里面的那堆xsd进行解析 @override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if (systemId != null) { if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } } return null; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwwsj.html