04加载xml到document过程
注:
1、可以把该篇文章复制到notepad++里,这样双击某个方法名,周边相同的也会高亮,方便理解整篇内容。
2、看代码时请在ClassPathXmlApplicationContext上按ctrl+t得到下面,然后用Snipaste截下来钉在屏幕右侧,
因为一边看代码一边发现有些方法穿插在各种中间类里,比如当你在看下面师傅里某个抽象方法比如
refreshBeanFactory,当你在refreshBeanFactory按ctrl+t不确定看哪个实现类时,此时从屏幕右侧钉住的截图
里就能发现应该看AbstractRefreshableApplicationContext
下面分析spring框架加载xml到document过程,先不要按面向对象去思考,把下面
当作c语言函数式调用看待,看明白流程后再从多场景角度(比如定义抽象方法loadBeanDefinitions和两个子类
AbstractXmlApplicationContext和AnnotationConfigWebApplicationContext可同时满足加载xml或加载注解两种
场景)去思考作者这么分层的好处。
下面的一到七是我后来补充的,所以一开始可以忽略一到七这几行文字,按照代码看明白后再看这几行文字。
下面是继承关系树,不用死记硬背,了解即可。
师傅 AbstractApplicationContext 大徒弟 AbstractRefreshableApplicationContext 二徒弟 AbstractRefreshableConfigApplicationContext 三徒弟 AbstractXmlApplicationContext 四徒弟 ClassPathXmlApplicationContext
//一、首先是主程序入口 public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Person p = context.getBean(Person.class); System.out.println(p.getName()); context.close(); } public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } //二、构造器里情况,里面步骤3是要看的 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { //1、创建ResourcePatternResolver对象(资源模式解析器) super(parent);//this.resourcePatternResolver = getResourcePatternResolver(); //getResourcePatternResolver()-->new PathMatchingResourcePatternResolver(this) //2、把带有占位符"${xxx}/application.xml"变成比如"/abc/application.xml"的形式 setConfigLocations(configLocations); //3、创建beanFactory对象、加载xml变为BeanDefinition对象、实例化bean对象 if (refresh) { refresh();//-->AbstractApplicationContext.refresh() } //通过上面看出框架使用模板方法模式,真正的程序入口是AbstractApplicationContext.refresh(), //公共的逻辑被refresh定死了,部分逻辑比如从xml //而ClassPathXmlApplicationContext增加了解析带有占位符的xml文件路径入参功能 } public void setConfigLocations(String[] locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } } protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path);// 把带有占位符"${xxx}/application.xml"变成比如"/abc/application.xml"的形式 //getEnvironment()-->new StandardEnvironment() } //三、进入父类的模板方法入口,里面的步骤B方法里的b1是要看的,不巧这是个抽象方法,交给大徒弟去做了 //AbstractApplicationContext.refresh() public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //1、定义一些标志位比如active为true和开始时间startupDate为当前时间 // Prepare this context for refreshing. prepareRefresh(); //2、 A、父类创建beanFactory对象、加载xml变为BeanDefinition对象并注册到beanFactory里 // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);//给beanFactory设置一堆众小弟,想看有哪些小弟来这个方法 try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory);//空方法 //通过方法doc可知该方法供开发者覆盖,可以在创建beanFactory对象之后做一些额外的工作, //比如往里注册MyXxBeanPostProcessor //3、提前实例化beanFactroy里所有 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 实例 //调用它们的方法修改beanFactory做个性化逻辑,参见附录 // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //通过方法doc可知调用所有BeanFactory后置处理器,参见下面附录A1。 //4、提前实例化beanFactory里所有 BeanPostProcessor,跟C同理通过getBean实例化并分为4组,加入到beanFactory的BeanPostProcessor属性里(这是个ArrayList<BeanPostProcessor>), //注:会先手动加入new BeanPostProcessorChecker(用于打印普通bean创建成功日志),等上面4组加入完成,最后再手动加入new ApplicationListenerDetector(探测普通bean如果实现了ApplicationListener接口,按照监听者模式这些bean就是监听器,把这些bean的引用放到一个地方,当有事件发生时,调用所有监听器) // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource();//通过doc可知注册什么消息源对象,暂时忽略 // Initialize event multicaster for this context. initApplicationEventMulticaster();//通过doc可知注册事件广播对象,暂时略 // Initialize other special beans in specific context subclasses. onRefresh();//通过doc可知这是父类提供给子类实现个性化功能的模板空方法,暂时略 // Check for listener beans and register them. registerListeners();//通过doc可知注册监听器对象,跟上面事件广播是配套的,暂时略 //实例化剩下所有普通bean(特指非lazy的bean) // Instantiate all remaining (non-lazy-init) singletons. ★finishBeanFactoryInitialization(beanFactory);★ //通过代码可知这里在做最后的beanFactory配置工作,先是设置conversionService属性 //,然后冻结beanFactory的所有BeanDefinition配置,最后预实例化所有bean。 // Last step: publish corresponding event. finishRefresh(); //通过代码可知这里在做最后的applicationContext的配置工作,先是初始化生 //命周期处理器,接着调用这些生命周期处理器的onRefresh方法,然后发布上 //下文刷新事件,最后往一个MBean里注册applicationContext对象,暂时略 } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } } protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //B1.创建beanFactory、加载xml变为BeanDefinition对象、实例化bean对象 refreshBeanFactory();//-->AbstractRefreshableApplicationContext.refreshBeanFactory() //B2. ConfigurableListableBeanFactory beanFactory = getBeanFactory();//-->AbstractRefreshableApplicationContext.getBeanFactory() if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } //B、大徒弟的名字带有可刷新,表示刷新beanFactory这事大徒弟包了,不需要父类去操心, //不巧的是大徒弟只管了刷新(即判断如果有了先销毁)和创建beanFactory实例这事,加载 //的事交给三师弟去做了 //大徒弟 //AbstractRefreshableApplicationContext.refreshBeanFactory() @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //B1_1.创建beanFactory对象 DefaultListableBeanFactory beanFactory = createBeanFactory();//-->new DefaultListableBeanFactory(null) beanFactory.setSerializationId(getId());//getId()就是this.toString() customizeBeanFactory(beanFactory);//给beanFactroy设置一堆属性组件供以后使用 //设置beanFactory的autowireCandidateResolver, //-->beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); //B1_2.加载xml变为BeanDefinition对象、实例化bean对象 loadBeanDefinitions(beanFactory);//-->AbstractXmlApplicationContext synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } //通过上面代码可知作者把刷新功能(即如果已经有了beanFactory要销毁重新创建)单独抽象到这一层代码里, //比如再有一个继承树ClassPathXmlApplicationContext2的父类叫不可刷新AbstractUnRefreshableApplicationContext, //这样刷新或不刷新这两种场景框架都能支持。 } //AbstractRefreshableApplicationContext.getBeanFactory() @Override public final ConfigurableListableBeanFactory getBeanFactory() { synchronized (this.beanFactoryMonitor) { if (this.beanFactory == null) { throw new IllegalStateException("BeanFactory not initialized or already closed - " + "call 'refresh' before accessing beans via the ApplicationContext"); } return this.beanFactory; } } //C、三师弟名字里带有xml,暗示他有能力从xml加载出BeanDefinition,但是从代码看出他把加载的事 //交给他的手下reader了 //三徒弟 //AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory) @Override 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);//就是当前applicationContext 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);//reader.setValidating(this.validating); //接上面 b1_2 loadBeanDefinitions(beanDefinitionReader); //通过上面代码可知作者把从xml加载BeanDefinition功能抽象到这一层代码里, //比如AnnotationConfigWebApplicationContext支持从注解加载BeanDefinition, //这样从xml加载或从注解加载这两种场景框架都能支持。 //从上面代码可知三徒弟把“加载xml变为BeanDefinition对象、实例化bean对象”的事交给 //XmlBeanDefinitionReader了,下面分析XmlBeanDefinitionReader } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources();//configResources实际为null if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations();//->AbstractRefreshableConfigApplicationContext.getConfigLocations() if (configLocations != null) { //接上面 b1_2 reader.loadBeanDefinitions(configLocations);//XmlBeanDefinitionReader.loadBeanDefinitions(String... locations) } } //二徒弟 //AbstractRefreshableConfigApplicationContext.getConfigLocations() protected String[] getConfigLocations() { return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); //this.configLocations就是一开始ClassPathXmlApplicationContext方法设置的属性,这里不为空 } //C_2、xmlReader这个小子开始努力干活了。。。 //XmlBeanDefinitionReader.loadBeanDefinitions ,其实是父类AbstractBeanDefinitionReader的方法 public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter; } public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, Set<Resource> actualResources) { ResourceLoader resourceLoader = getResourceLoader();//-->就是当前applicationContext if (resourceLoader instanceof ResourcePatternResolver) {//会进入这里 // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } 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. org.springframework.core.io.Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource);//-->XmlBeanDefinitionReader.loadBeanDefinitions(resource) if (actualResources != null) { actualResources.add(resource); } return loadCount; } } //XmlBeanDefinitionReader.loadBeanDefinitions(resource) public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { //略... InputStream inputStream = encodedResource.getResource().getInputStream(); //最终把xml文件变成流再变成jdk里的inputSource,这弯绕的 org.xml.sax.InputSource inputSource = new InputSource(inputStream); return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } //C_3、xmlReader只是把文件变成inputSource,自知能力有限,交给了他的好友documentLoader(DefaultDocumentLoader), //到此xml变为document的流程完毕。 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { //略... int validationMode = getValidationModeForResource(resource); // Document doc = this.documentLoader.loadDocument(//documentLoader-->new DefaultDocumentLoader(); inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount();//getRegistry()-->beanFactory ★documentReader.registerBeanDefinitions(doc, createReaderContext(resource));★ return getRegistry().getBeanDefinitionCount() - countBefore; } protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { //返回的是 DefaultBeanDefinitionDocumentReader return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); } protected XmlReaderContext createReaderContext(Resource resource) { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); //createDefaultNamespaceHandlerResolver-->new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader()); } return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.namespaceHandlerResolver); } //getRegistry().getBeanDefinitionCount() DefaultListableBeanFactory.getBeanDefinitionCount(){ return this.beanDefinitionMap.size(); } //DefaultBeanDefinitionDocumentReader.registerBeanDefinitions public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root);//这个比较复杂,下面单另写流程 } //这里解析xml采用jdk自带的组件, //该组件用法请参考:https://blog.csdn.net/NHB456789/article/details/135244542 //即先调用静态方法获得该工厂实例,然后创建 DocumentBuilder 对象 //总之这么啰嗦的就是为了得到Document对象 //DefaultDocumentLoader.loadDocument() public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); } protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) { factory.setValidating(true); if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { // Enforce namespace aware for XSD... factory.setNamespaceAware(true); try { factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE); } catch (IllegalArgumentException ex) { ParserConfigurationException pcex = new ParserConfigurationException( "Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " + "Upgrade to Apache Xerces (or Java 1.5) for full XSD support."); pcex.initCause(ex); throw pcex; } } } return factory; } //DocumentBuilderFactory.newInstance() //该方法根据如下3个优先级策略创建该工厂实例对象 //1、根据javax.xml.parsers.DocumentBuilderFactory系统环境变量的值创建工厂实例对象 //2、从java_home/lib/jaxp.properties里的javax.xml.parsers.DocumentBuilderFactory // 环境变量值创建工厂实例对象 //3、基于ServiceLoader.load(type)方法即jdk的spi机制(可以阅读ServiceLoader的doc学习 // 下这种机制,即提前定义一个接口a.MyIfc,假定有两个子类,然后jar包里定义文本文件 // ,路径META-INF/services/a.MyIfc,内容为a.MyIfcImpl1一行,a.MyIfcImpl2一行, // 然后调用ServiceLoader.load(a.MyIfc).iterator()就可以依次得到两个实现类的实例 // 对象)来获得javax.xml.parsers.DocumentBuilderFactory实现类的实例对象 //不巧上面3个都不满足,最终实例化DocumentBuilderFactoryImpl对象并返回 public static DocumentBuilderFactory newInstance() { return FactoryFinder.find(//该方法支持通过系统环境变量DocumentBuilderFactory /* The default property name according to the JAXP spec */ DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory" /* The fallback implementation class name */ "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); }
大的流程:
1、把带有占位符的xml路径变成正常的路径
2、创建beanFactory(内部加载xml为BeanDefinition,然后实例化bean)
3、给beanFactory设置一堆众小弟
4、调用2种所有beanFactory后置处理器(分别做个性化注册BeanDefinition、个性化修改beanFactory操作)
5、注册所有bean后置处理器
6、初始化消息功能
7、初始化事件广播功能
8、注册监听器
9、其他beanFactory和applicationContext收尾工作
附录:
A1、invokeBeanFactoryPostProcessors方法逻辑:
1、调用appliationContext的所有 BeanDefinitionRegistryPostProcessor 对象
的 postProcessBeanDefinitionRegistry(registry)方法//registry就是beanFactory
这里的BeanDefinitionRegistryPostProcessor 对象肯定是main方法里手动添加到applicationContext里的。
实现开发者针对BeanFactory做注册BeanDefinition的个性化需求
2、通过beanFactory.getBeansOfType方法里的getBean方法把beanFactory里的 BeanDefinitionRegistryPostProcessor 类型提前实例化,
注:这个getBean方法就是实例化方法入口,里面太复杂,后面文章细说。
调用beanFactory的所有 BeanDefinitionRegistryPostProcessor 对象
的 postProcessBeanDefinitionRegistry(registry)方法,
这里的BeanDefinitionRegistryPostProcessor 对象是xml里定义的。
实现开发者针对BeanFactory做注册BeanDefinition的个性化需求
3、调用appliationContext的所有 BeanDefinitionRegistryPostProcessor 对象
的 postProcessBeanFactory(beanFactory)方法
实现开发者修改BeanFactory的个性化需求
4、调用beanFactory的所有 BeanDefinitionRegistryPostProcessor 对象
的 postProcessBeanFactory(beanFactory)方法
实现开发者修改BeanFactory的个性化需求
5、调用appliationContext的所有 BeanFactoryPostProcessor 对象
的 postProcessBeanFactory(beanFactory)方法
实现开发者修改BeanFactory的个性化需求
6、调用beanFactory.getBeanNamesForType获得所有BeanFactoryPostProcessor的name,
遍历这些name,根据name对应的BeanDefinition里的类型把这些后置处理器分成3组,遍历的同时通过getBean实例化出bean,
按优先级PriorityOrdered接口、Ordered接口、无顺序三种归类
6.1、对优先级类型的集合排序后依次调用 postProcessBeanFactory(beanFactory)方法
6.2、对Ordered类型的集合排序后依次调用 postProcessBeanFactory(beanFactory)方法
6.3、对无顺序类型的集合依次调用 postProcessBeanFactory(beanFactory)方法
即调用beanFactory的所有 BeanFactoryPostProcessor 对象
的 postProcessBeanFactory(beanFactory)方法
实现开发者修改BeanFactory的个性化需求
注:
上面比较啰嗦,简单总结上面策略为调用 BeanDefinitionRegistryPostProcessor 对象
的postProcessBeanDefinitionRegistry(registry)方法【个性化注册BeanDefinition】
和postProcessBeanFactory(beanFactory)方法【个性化修改beanFactory】,
然后调用 BeanFactoryPostProcessor 对象
的postProcessBeanFactory(beanFactory)方法【个性化修改beanFactory】,
整体上优先调用application的beanFactory后置处理器再调用beanFactory的beanFactory后置处理器。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战