spring 源码学习笔记(一)—— spring ioc 之加载XML转换为BeanDefinition
来源:互联网 发布:u盘windows无法格式化 编辑:程序博客网 时间:2024/05/19 22:50
欢迎访问我的个人博客休息的风
spring ioc容器的核心类是AbstractApplicationContext,入口方法是refresh。这个方法是个模板方法,定义了加载到容器的全部过程。本篇博客将分析,spring将xml配置文件加载到内存的一个过程。(著名的dubbo分布式框架也利用了spring加载xml的机制,定制自己的xml解析器将对象接入到ioc容器中。)大致过程为:创建beanFactory用于存放转换后的信息->读取文件到输入流中->读取输入流里的数据,用NamespaceHandler里注册的解析器处理返回BeanDefinition->将BeanDefinition保存到DefaultListableBeanFactory的beanDefinitionMap中。
以下是整个调用过程的时序图,最终xml在spring中会放到一个以名称为key,beanDefinition为value的ConcurrentHashMap中。将根据这个时序图,逐步分析对应的源码,希望能把spring加载xml这一过程解释清楚(图象看不清可右击在新页签中查看)
首先,在AbstractApplicationContext.refresh方法中,作为spring初始化的全部过程定义。obtainFreshBeanFactory这个方法是开始解析xml文件的入口
@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //省略很多代码。。。。。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //在AbstractRefreshableApplicationContext中去创建DefaultListableBeanFactory类 //这个DefaultListableBeanFactory可以当作是存放ioc容器的地方 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory;}
具体如何创建DefaultListableBeanFactory的,在子类AbstractRefreshableApplicationContext中实现
protected final void refreshBeanFactory() throws BeansException { //是否存在bean工厂类,这个工厂类指的是DefaultListableBeanFactory if (hasBeanFactory()) { //销毁工厂类里面的bean destroyBeans(); //工厂类设置为null closeBeanFactory(); } try { //创建DefaultListableBeanFactory对象 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //设置工厂类一些参数值 customizeBeanFactory(beanFactory); //真正开始加载xml,转换为beandefinition的入口 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } }
在AbstractXmlApplicationContext会去调用loadBeanDefinitions,去读取还是交由XmlBeanDefinitionReader类处理
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //以bean工厂类为参数创建一个读取器,之后读取的内容还是放到工厂类里 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); //交由beanDefinitionReader去读取文件内容 loadBeanDefinitions(beanDefinitionReader);}
在XmlBeanDefinitionReader类里,会去把文件转换为文件输入流。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { //省略一些代码 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(); } }
接下来读取输入流里面的信息,用BeanDefinitionDocumentReader(DefaultBeanDefinitionDocumentReader)的registerBeanDefinitions方去去读取。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //创建文件Document读取器 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //这里的getRegistry()其实也是获取DefaultListableBeanFactory int countBefore = getRegistry().getBeanDefinitionCount(); //用文件Document读取器读取,并把解析的BeanDefinition放到DefaultListableBeanFactory里 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;}
在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法里,会去创建BeanDefinitionParserDelegate对象。需要特别关注这个类,因为这个类会去调用对应的NamespaceHandler里面注册的解析器去解析。(dubbo里面DubboNamespaceHandler就是在这里与spring结合在一起的)
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); //省略一些代码。。 preProcessXml(root); //解析xml配置文件里的配置,转换为beanDefinition, // 并用BeanDefinitionReaderUtils注册到DefaultListableBeanFactory里 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent;}
在解析xml的时候,会委托BeanDefinitionParserDelegate去做,这里真正处理xml里面的配置具体内容的,是注册在NamespaceHandler里面的Parser解析器。
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } //这里根据namespaceUri去获取对应的NamespaceHandler去做处理 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}
NamespaceHandlerSupport类有对NamespaceHandler抽象的统一处理。通过NamespaceHandlerSupport.findParserForElement方法找到之前注册到NamespaceHandler的BeanDefinitionParser
public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); }}
解析之后,将xml里面的数据处理成beanDefinition后,统一通过BeanDefinitionReaderUtils.registerBeanDefinition注册到DefaultListableBeanFactory的beanDefinitionMap中。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //这个registry就是DefaultListableBeanFactory //放入到beanDefinitionMap中 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } }}
至此,就将xml配置文件里的配置信息转换为DefaultListableBeanFactory的beanDefinitionMap里面的信息。
开篇讲到的整个过程为:1、创建beanFactory用于存放转换后的信息->2、读取文件到输入流中->3、读取输入流里的数据,用NamespaceHandler里注册的解析器处理返回BeanDefinition->4、将BeanDefinition保存到DefaultListableBeanFactory的beanDefinitionMap中。再回顾下整个代码过程,与开篇的对应如下:
1、在AbstractApplicationContext.refresh这个方法里,作为创建beanFactory的入口。
2、在XmlBeanDefinitionReader.loadBeanDefinitions,会去把文件转换为文件输入流。
3、BeanDefinitionParserDelegate.parseCustomElement这个类的方法会去调用对应的NamespaceHandler里面注册的解析器去解析
4、BeanDefinitionReaderUtils.registerBeanDefinition这个方法里,会把BeanDefinition注册到BeanFactory中
- spring 源码学习笔记(一)—— spring ioc 之加载XML转换为BeanDefinition
- spring IOC源码学习(二):BeanDefinition资源加载
- spring 源码学习笔记(二)—— spring ioc 之依赖注入
- Spring 源码之 BeanDefinition阅读
- spring源码学习之路---IOC初探(一)
- Spring学习笔记二之IOC(xml实现)
- Spring学习笔记(二)-----IOC之XML
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册
- Spring源码学习(一)------ IoC
- Spring源码学习(一)------ IoC
- Spring源码学习(一)------ IoC
- Spring IOC核心源码学习(一)
- Spring源码学习(一)------ IoC
- Spring源码学习(一)------ IoC .
- Spring源码学习(一)------ IoC
- Spring源码学习(一)------ IoC
- Spring IOC核心源码学习(一)
- Spring IOC核心源码学习(一)
- 原型链模式扩展
- XSS浅学习
- 剑指offer之空格替换(Python)
- Linux常用指令
- HttpURLConnection 请求
- spring 源码学习笔记(一)—— spring ioc 之加载XML转换为BeanDefinition
- 论文管理方法
- Failed to resolve: com.android.support:26.X.X 问题的解决
- 基于Python安装TensorFlow、库安装和Jupyter Notebook
- php静态页面局部动态化
- 周六周日还不闲着,加油学习
- 【Flask】Flask的用途和目的
- 虚拟机的三种网络模式
- HTTP、TCP、Session、cookie、