• Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory


    前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-AbstractApplicationContext
    约束:

    1. 本文指定contextClass为默认的XmlWebApplicationContext
    2. 从属AbstractApplicationContext#refresh方法

    AbstractApplicationContext#obtainFreshBeanFactory

    该方法主要完成创建Bean工厂,涉及到解析spring文件,代码清单如下

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    		//更新bean工厂,其会销毁已存在的bean内容并重新创建
    		refreshBeanFactory();
    		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    		if (logger.isDebugEnabled()) {
    			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    		}
    		return beanFactory;
    	}
    

    由以上代码所见,关键方法指向子类AbstractRefreshableApplicationContext#refreshBeanFactory,代码清单如下

    protected final void refreshBeanFactory() throws BeansException {
    		//存在已有bean工厂则销毁
    		if (hasBeanFactory()) {
    			destroyBeans();
    			closeBeanFactory();
    		}
    		try {
    			//创建默认的用List接口存放bean的工厂
    			DefaultListableBeanFactory beanFactory = createBeanFactory();
    			//这里同contextId
    			beanFactory.setSerializationId(getId());
    			//配置allowBeanDefinitionOverriding和allowCircularReferences属性,这里均不设置
    			customizeBeanFactory(beanFactory);
    			//调用子类的加载bean定义方法,这里会调用XmlWebApplicationContext子类的复写方法
    			loadBeanDefinitions(beanFactory);
    			synchronized (this.beanFactoryMonitor) {
    				this.beanFactory = beanFactory;
    			}
    		}
    		catch (IOException ex) {
    			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    		}
    	}
    

    XmlWebApplicationContext#loadBeanDefinitions

    加载bean预方法,代码清单如下

    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.
    		//环境为StardEvironment对象
    		beanDefinitionReader.setEnvironment(getEnvironment());
    		//资源加载器为XmlWebApplicationContext
    		beanDefinitionReader.setResourceLoader(this);
    		//实体分解器为ResourceEntityResolver对象,主要用于javax.xml.parse解析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);
    		//真切的开始加载bean
    		loadBeanDefinitions(beanDefinitionReader);
    	}
    

    紧接着查看真实的加载bean方法XmlWebApplicationContext#loadBeanDefinitions(XmlBeanDefinitionReader reader),代码清单如下

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    		//获取spring配置文件的位置列表
    		String[] configLocations = getConfigLocations();
    		if (configLocations != null) {
    			for (String configLocation : configLocations) {
    				//读取加载
    				reader.loadBeanDefinitions(configLocation);
    			}
    		}
    	}
    

    AbstractBeanDefinitionReader#loadBeanDefinitions

    真实的加载bean内容可追溯到XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader#loadBeanDefinitions(String location, Set<Resource> actualResources)方法,代码清单如下

    //此处的actualResources参数传过来为null
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    		//此处的ResourceLoader为XmlWebApplicationContext
    		ResourceLoader resourceLoader = getResourceLoader();
    		if (resourceLoader == null) {
    			throw new BeanDefinitionStoreException(
    					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    		}
    		//XmlWebApplicationContext符合此条件
    		if (resourceLoader instanceof ResourcePatternResolver) {
    			// Resource pattern matching available.
    			try {
    				//调用的是AbstractApplicationContext的getResources方法,追溯一下调用的其实是PathMatchingResourcePatternResolver.getResources方法,其会搜寻指定目录符合条件的文件集合
    				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    				//加载某个spring bean文件
    				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;
    		}
    	}
    

    略过一些代码调用,直接看XmlBeanDefinitionReader#registerBeanDefinitions(Document doc,Resource resource)注册bean定义方法,代码清单如下

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    		documentReader.setEnvironment(this.getEnvironment());
    		//此处的getRegistry()方法返回的实例为DefaultListableBeanFactory类型
    		int countBefore = getRegistry().getBeanDefinitionCount();
    		//调用DefaultBeanDefinitionDocumentReader.registerBeanDefinitions方法
    		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    		return getRegistry().getBeanDefinitionCount() - countBefore;
    	}
    

    DefaultBeanDefinitionDocumentReader#registerBeanDefinitions

    代码清单如下

    	//解析bean定义
    	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    		this.readerContext = readerContext;
    		logger.debug("Loading bean definitions");
    		//此处的root节点一般为<beans>节点
    		Element root = doc.getDocumentElement();
    		//具体解析的方法
    		doRegisterBeanDefinitions(root);
    	}
    

    解析bean具体方法DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions,代码清单如下

    protected void doRegisterBeanDefinitions(Element root) {
    		//此处针对<beans>的节点属性profile进行的操作
    		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    		if (StringUtils.hasText(profileSpec)) {
    			Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
    			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
    					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    			if (!this.environment.acceptsProfiles(specifiedProfiles)) {
    				return;
    			}
    		}
    
    		// Any nested <beans> elements will cause recursion in this method. In
    		// order to propagate and preserve <beans> default-* attributes correctly,
    		// keep track of the current (parent) delegate, which may be null. Create
    		// the new (child) delegate with a reference to the parent for fallback purposes,
    		// then ultimately reset this.delegate back to its original (parent) reference.
    		// this behavior emulates a stack of delegates without actually necessitating one.
    		BeanDefinitionParserDelegate parent = this.delegate;
                    //读取<beans>标签中的default-*属性
    		this.delegate = createDelegate(this.readerContext, root, parent);
    		//预处理bean xml配置文件中的自定义标签,默认是为空的
    		preProcessXml(root);
    		//解析bean xml配置文件
    		parseBeanDefinitions(root, this.delegate);
    		//处理bean xml配置文件中的自定义标签,默认是为空的
    		postProcessXml(root);
    
    		this.delegate = parent;
    	}
    

    从上述的注释可知,我们只需关注DefaultBeanDefinitionDocumentReader#parseBeanDefinitions对bean xml文件的解析,鉴于其重要性,将于下一章节详细解读

    下节预告

    Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

  • 相关阅读:
    计算机网络
    计算机网络知识总结
    final,static,super,this
    ArrayList
    基础面试题
    BATJ都爱问的多线程面试题
    Session过期、失效时间
    类加载器ClassLoader源码解析
    连接ftp服务器 JDK 1.7
    根据当前请求的特征,判断该请求是否来自手机终端
  • 原文地址:https://www.cnblogs.com/question-sky/p/6704073.html
Copyright © 2020-2023  润新知