ClassPathXmlApplicationContext源码解析一:准备工作
来源:互联网 发布:有价值的域名 编辑:程序博客网 时间:2024/05/21 14:05
本文引自我的个人博客: sunmingshuai.coding.me
其实在web环境中 用到的上下文环境是 XmlWebApplicationContext
但其实对于我们要讲解的内容来说 核心逻辑是一样的 在后面讲解到web环境加载的时候 会讲到XmlWebApplicationContext
类的解析 下面几篇博客的内容我们会介绍Spring
是怎么构建上下文环境的 也就是ClassPathXmlApplicationContext
的源码的过程 ClassPathXmlApplicationContext
类的测试类是ClassPathXmlApplicationContextTests
如果读者能好好利用这个测试类的话 能节省不少阅读源码的时间
构造函数
//接收配置文件的地址 默认刷新 父容器为null 讲解web环境加载的时候 会讲到父容器的概念 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
以后讲解web环境加载的时候我们会讲到父子容器的概念 这里只考虑父容器为null的情况 如果路径中含有如${user.dir}
的变量 会被处理替换掉 对替换这部分感兴趣的读者可以深入阅读 然后就进入最重要的一个方法refresh()
refresh() refresh()
方法从总体上看 还算比较清晰 用几个大的方法高度概括了refresh()
要完成的工作
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 加载前的准备工作:active标记为true;资源必要性验证等 prepareRefresh(); // 创建DefaultListableBeanFactory 并加载配置文件 转化为BeanDefinition保存 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { //empty postProcessBeanFactory(beanFactory); // spring扩展的实现(容器级别) BeanFactoryPostProcessor 在实例化任何用户定义的bean之前 会首先调用BFPP的接口方法 // 常见的BFPP:PropertyPlaceholderConfigurer invokeBeanFactoryPostProcessors(beanFactory); // spring可扩展的另一个实现:BeanPostProcessor 在调用bean的init-method方法的前后会调用接口方法 // 较常见的硬编码的BPP:ApplicationContextAwareProcessor,ApplicationListenerDetector registerBeanPostProcessors(beanFactory); //国际化 initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //empty onRefresh(); // Check for listener beans and register them. // 注册listener registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有的非懒惰加载的单例 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
下面我们看obtainFreshBeanFactory()#AbstractRefreshableApplicationContext.refreshBeanFactory()
方法(obtainFreshBeanFactory()
方法中主要逻辑是在refreshBeanFactory()
方法 我们就不再列出完整的obtainFreshBeanFactory()
方法 以后这样的格式我们用#
区分)
再此感叹spring
代码的优秀 每个方法都是那么的简短清晰 一层一层的深入
protected final void refreshBeanFactory() throws BeansException { // 如果已经加载过的话 先销毁再重新加载 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //实例化一个DefaultListableBeanFactory 这是一个很重要的类 DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置id 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); } }
/** * 忽略BeanNameAware BeanFactoryAware BeanClassLoaderAware的依赖 spring有设置专门调用setXXX方法的地方 * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
对于这样的代码 如果不跟踪进去的话 可能会对有些变量的值感到困惑 createBeanFactory()
实例化了一个DefaultListableBeanFactory
在实例化的过程中 其父类 AbstractAutowireCapableBeanFactory
的构造函数注册了几个忽略依赖注入的接口 就是说以后对于bean的属性注入的时候 如果在我们注册的忽略接口里面的话 就不再依赖注入
// protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { //是否允许覆盖 if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } //是否允许循环引用 循环引用是很重要的一个知识点 我们后面会专门讲解 if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } //设置Qualifier注解解析类 beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); }
我们看下beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver())
方法 这个方法就是设置autowireCandidateResolver
变量 顺便做了一件事情 就是如果该变量继承了BeanFactoryAware
的话 调用setBeanFactory()
方法 我们从这里就可以看到sping
代码优秀的另一个方面是约定习俗
public void setAutowireCandidateResolver(final AutowireCandidateResolver autowireCandidateResolver) { Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null"); if (autowireCandidateResolver instanceof BeanFactoryAware) { if (System.getSecurityManager() != null) { final BeanFactory target = this; AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(target); return null; } }, getAccessControlContext()); } else { // 调用`setBeanFactory()`方法 this作为参数 ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(this); } } //设置变量 this.autowireCandidateResolver = autowireCandidateResolver; }
下面就进入很重要的一个方法了AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory);
载入配置文件 就是根据配置文件做相应的处理 例如将<bean>
转化为BeanDefinition保存 这跟Tomcat将server.xml
转化为StandardServer
是一样的 阅读这部分代码并不需要你很了解是怎么解析xml文档的 因为方法包装的很好的 当然如果读者比较了解xml文档解析的话 那么就更好了
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); }
tips 前面几行代码一直在做准备工作 刚阅读源码的读者一定会被这错综复杂的关系所困扰 spring
中模块众多,继承 组合关系也是错综复杂 第一次阅读的话 读者可不必太在意这些类之间的委托关系 跟着代码走就可以了 以后阅读第二遍第三遍的时候 再好好研究 比如到现在为止我们都是顺着代码走 并没有从代码设计的角度去讲解 loadBeanDefinitions()
方法最终会调用AbstractBeanDefinitionReader#loadBeanDefinitions()
方法
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { //ClassPathXmlApplicationContext ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 把String类型的地址封装成Resource类型 方便后续操作 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
方法调用AbstractBeanDefinitionReader#loadBeanDefinitions(resource)#loadBeanDefinitions(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(); } } }
也是做各种准备工作 终于让我们遇到了我们想要看的代码doLoadBeanDefinitions
代码读到后面 读者就会注意到spring
中以do开头的方法才是真正的核心逻辑的开始
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //获取验证模式 dtd or xsd int validationMode = getValidationModeForResource(resource); //为SAX应用程序提供寻找本地验证文件(dtd/xsd文件) 并将配置文件加载为document保存 Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } ...... }
当然还是山路十八弯
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); // documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //解析的核心部分开始 doRegisterBeanDefinitions(root); }
protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//profile attribute if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
在方法createDelegate(this.readerContext, root, parent);
中 通过populateDefaults()
方法设置了一些例如default-lazy-init
default-autowire
的值 如果根元素中有设置这些变量的话 将会覆盖默认值 起到全局变量的作用 preProcessXml(root)
postProcessXml(root)
都被留空 留给子类实现
终于 finally 最终 我们看到了我们一直想看到的代码的影子 parseBeanDefinitions(root, this.delegate)
要知后事如何 且听下回分解
- ClassPathXmlApplicationContext源码解析一:准备工作
- ClassPathXmlApplicationContext 源码解析
- ClassPathXmlApplicationContext源码解析四
- ClassPathXmlApplicationContext源码解析三:BFPP
- Spring 源码梳理(一) ClassPathXmlApplicationContext
- spring源码解析-准备工作
- Spring IOC 源码-ClassPathXmlApplicationContext-bean解析
- ClassPathXmlApplicationContext源码解析五:加载单例
- Spring源码学习--ClassPathXmlApplicationContext(一)
- ClassPathXmlApplicationContext源码解析二:默认空间元素解析
- spring源码解析之一(准备工作篇)
- SOLR源码学习之准备工作(一)
- Struts2源码学习(一)准备工作
- Gh0st源码学习(一)前期准备工作
- Spring4源码分析(ClassPathXmlApplicationContext)
- flask源码笔记:一,阅读Flask源码前的准备工作
- 一 ServletContext 和 spring ClassPathXmlApplicationContext
- ClassPathXmlApplicationContext
- HihoCoder1151 骨牌覆盖问题·二(矩阵快速幂,递推)
- 使用LM386制作Arduino音乐播放器
- 机器学习笔记之高级优化
- LintCode 151 买卖股票的最佳时机 III
- RBTree的插入算法
- ClassPathXmlApplicationContext源码解析一:准备工作
- 欢迎使用CSDN-markdown编辑器
- Qt编译问题-向导生成的helloworld都编译出错
- 2017暑假七林集训day24
- MySQL插入中文乱码
- ClassPathXmlApplicationContext源码解析二:默认空间元素解析
- ZCMU1901-LOGO
- mobilenet
- 基于markdown的blog系统调研1:typecho