zoukankan      html  css  js  c++  java
  • Spring源码02---obtainFreshBeanFactory

    容器刷新前配置: https://www.cnblogs.com/xiaomaomao/p/14046219.html

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    		// The application context id is still set to its original default value
    		// -> assign a more useful id based on available information
    		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
    		if (idParam != null) {
    			wac.setId(idParam);
    		}
    		else {
    			// Generate default id...
    			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
    					ObjectUtils.getDisplayString(sc.getContextPath()));
    		}
    	}
    
    	wac.setServletContext(sc);
    	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    	if (configLocationParam != null) {
    		wac.setConfigLocation(configLocationParam);
    	}
    
    	// The wac environment's #initPropertySources will be called in any case when the context
    	// is refreshed; do it eagerly here to ensure servlet property sources are in place for
    	// use in any post-processing or initialization that occurs below prior to #refresh
    	ConfigurableEnvironment env = wac.getEnvironment();
    	if (env instanceof ConfigurableWebEnvironment) {
    		((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    	}
    
    	customizeContext(sc, wac);
    	// 调用 AbstractApplicationContext 的 refresh 方法
    	wac.refresh();
    }
    

    wac 是 XmlWebApplicationContext 类型的, XmlWebApplicationContext 的继承结构如下:

     

    这里的 wac 是 XmlWebApplicationContext 类型的,在它本类中没有找到 refresh() 方法,那么就往上找,结果在这张图的最上层接口 ConfigurableApplicationContext 中找到了 refresh() 方法,而ConfigurableApplicationContext 接口的实现类中只有 AbstractApplicationContext 这个抽象类实现了 refresh() 方法,所以我们最终会去到 AbstractApplicationContext 类中的 refresh()

    找到 AbstractApplicationContext 类中的 refresh() 方法,我们主要看一下这个方法到底做了什么?

    public void refresh() throws BeansException, IllegalStateException {
    	// 同步锁,如果 refresh() 方法还没有执行完成,这个时候你突然继续再来一次容器启动或者容器销毁的动作,那么就乱套了
    	synchronized (this.startupShutdownMonitor) {
    
    		prepareRefresh();
    
    		// 告知子类刷新内部的 Bean Factory
    		// 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,并注册到 BeanFactory 中.
            // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
            // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
    		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    	}
    	......
    }

    整个 refresh() 方法是 Spring IOC 的核心方法,可以看到 refresh 里面有很多的方法,但是我们这里探究的是刷新 BeanFactory 的操作,看看刷新 BeanFactory 的时候到底执行了哪一些的操作 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

    首先我们来到 obtainFreshBeanFactory() 方法

    代码块一、obtainFreshBeanFactory()

    // AbstractApplicationContext 类中的方法
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    	// 1、刷新 BeanFactory,由 AbstractRefreshableApplicationContext 实现----(详细见代码块二)
    	refreshBeanFactory();
    	// 2、获取刚刚创建的 BeanFactory
    	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    	if (logger.isDebugEnabled()) {
    		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    	}
    	// 3、返回 BeanFactory
    	return beanFactory;
    }
    

    代码块二、refreshBeanFactory()

    // AbstractRefreshableApplicationContext 类中的方法
    protected final void refreshBeanFactory() throws BeansException {
    	// 如果 ApplicationContext 中已经加载过了 BeanFactory ,销毁所有 Bean ,关闭 BeanFactory
    	// 注意:应用中 BeanFactory 本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前的
    	// ApplicationContext 是否持有 BeanFactory
    	if (hasBeanFactory()) {
    		destroyBeans();
    		closeBeanFactory();
    	}
    	try {
    		// 2、创建 BeanFactory ,为什么那么多的 IOC 容器,单单要创建 DefaultListableBeanFactory 呢?这个下面会说到
    		DefaultListableBeanFactory beanFactory = createBeanFactory();
    		// 3、设置序列化 ID
    		beanFactory.setSerializationId(getId());
    		// 4、设置 BeanFactory 的两个属性,是否允许 bean 覆盖、是否允许循环引用
    		customizeBeanFactory(beanFactory);
    		// 5、加载 Bean 定义----(详细见代码块三)
    		loadBeanDefinitions(beanFactory);
    		synchronized (this.beanFactoryMonitor) {
    			this.beanFactory = beanFactory;
    		}
    	}
    	catch (IOException ex) {
    		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    	}
    }

    createBeanFactory() 这个步骤返回的是一个 DefaultListableBeanFactory 类型,我们都知道 Spring 中有众多的 IOC 容器,为什么这里创建的是 DefaultListableBeanFactory 呢?我们可以通过下面这张继承关系图可以得出结论

    通过上面这张图我们可以看出 ConfigurableListableBeanFactory 实现了第二层级的三个接口,并且它只有一个实现类 DefaultListableBeanFactory ,DefaultListableBeanFactory 这个实现类又通过继承 AbstractAutowireCapableBeanFactory 抽象类把整张图的功能都囊括了,这是其它的 BeanFactory 都做不到的,所以既然它的功能是最全的,这就是我们选择 DefaultListableBeanFactory 作为 BeanFactory 的实现类的原因.

    代码块三、loadBeanDefinitions(beanFactory)

    // XmlWebApplicationContext 中的方法
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    	// 1、为指定的 beanFactory 创建一个 XmlBeanDefinitionReader
    	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    	// 2、设置环境信息
    	beanDefinitionReader.setEnvironment(getEnvironment());
    	// 3、将 XmlWebApplicationContext 赋值给 beanDefinitionReader 的 resourceLoader 属性
    	// XmlWebApplicationContext 实现了 ResourceLoader 接口
    	beanDefinitionReader.setResourceLoader(this);
    	// 4、设置实体解析器
    	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    	// Allow a subclass to provide custom initialization of the reader,
    	// then proceed with actually loading the bean definitions.
    	// 5、空方法,留给子类去重写的方法
    	initBeanDefinitionReader(beanDefinitionReader);
    	// 6、加载 BeanDefinition(核心方法) ----(详情见代码块四)
    	loadBeanDefinitions(beanDefinitionReader);
    }
    

    代码块四、loadBeanDefinitions(beanDefinitionReader)

    // XmlWebApplicationContext 中的方法
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    	// 1、获取配置文件路径,如果 web.xml 中配置了 contextConfigLocation ,使用它的值作为 Spring 配置文件路径
    	// 如果没有配置 contextConfigLocation ,那么就使用 spring 默认的配置文件 /WEB/INF/application.xml
    	// (详情见代码块五)
    	String[] configLocations = getConfigLocations();
    	if (configLocations != null) {
    		// 2、遍历配置文件路径,因为配置文件的路径可以指定多个
    		for (String configLocation : configLocations) {
    			// 3、根据其中的一个配置文件路径加载 BeanDefinitions ----(详情见代码块六)
    			reader.loadBeanDefinitions(configLocation);
    		}
    	}
    }
    

    代码块五、getConfigLocations()

    // AbstractRefreshableWebApplicationContext 类中的方法
    public String[] getConfigLocations() {
    	// 1、调用父类方法
    	return super.getConfigLocations();
    }
    
    // AbstractRefreshableConfigApplicationContext 类中的方法
    protected String[] getConfigLocations() {
    	// 1、如果 web.xml 中配置了 contextConfigLocation ,使用该参数对应的值作为 spring 的配置文件
    	// 2、如果没有配置,则使用 getDefaultConfigLocations()
    	return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
    }
    
    // XmlWebApplicationContext 类中的方法
    protected String[] getDefaultConfigLocations() {
    	if (getNamespace() != null) {
    		return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    	}
    	else {
    		// spring 默认的配置文件路径 /WEB/INF/application.xml
    		return new String[] {DEFAULT_CONFIG_LOCATION};
    	}
    }

    代码块六、loadBeanDefinitions(configLocation)

    // AbstractBeanDefinitionReader 类中的方法
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    	// 1、调用本类中两个参数的构造方法,第二个参数值为 null ---- (详情见代码块七)
    	return loadBeanDefinitions(location, null);
    }
    

    代码块七、loadBeanDefinitions(location, null)

    // AbstractBeanDefinitionReader 类中的方法
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    	// 1、获取 ResourceLoader ,我们这里是 XmlWebApplicationContext
    	ResourceLoader resourceLoader = getResourceLoader();
    	// 2、如果类加载器为空,抛出异常
    	if (resourceLoader == null) {
    		throw new BeanDefinitionStoreException(
    				"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    	}
    	// 3、判断 resourceLoader 是否为 ResourcePatternResolver 的实例
    	if (resourceLoader instanceof ResourcePatternResolver) {
    		try {
    			// 3.1、将 String 类型的配置文件名根据路径、前后缀等进行匹配获取到符合条件的配置文件,然后转成 Resource 类型的数组
    			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
          // 3.2、根据 resources 加载 BeanDefinitions ---- (详情见代码块八)
    			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 + "]");
    			}
    			// 3.3、返回加载 BeanDefinition 的个数
    			return loadCount;
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException(
    					"Could not resolve bean definition resource pattern [" + location + "]", ex);
    		}
    	}
    	else {
    		// 4、通过绝对路径来加载资源,因为是绝对路径,只能加载一个配置文件
    		Resource resource = resourceLoader.getResource(location);
    		// 5、通过绝对路径来加载 BeanDefinitions
    		int loadCount = loadBeanDefinitions(resource);
    		if (actualResources != null) {
    			actualResources.add(resource);
    		}
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
    		}
    		return loadCount;
    	}
    } 

    代码块八、loadBeanDefinitions(resources)

    // AbstractBeanDefinitionReader 类中的方法
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    	Assert.notNull(resources, "Resource array must not be null");
    	int counter = 0;
    	for (Resource resource : resources) {
    		// 1、根据单个 Spring 配置文件加载 BeanDefinitions ---- (详情见代码块九)
    		counter += loadBeanDefinitions(resource);
    	}
    	// 2、返回最后总共加载的 BeanDefinition
    	return counter;
    }

    代码块九、loadBeanDefinitions(resource)

    // XmlBeanDefinitionReader 类中的方法
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    	// 1、加载 BeanDefinition ---- (详情见代码块十)
    	return loadBeanDefinitions(new EncodedResource(resource));
    }

    代码块十、loadBeanDefinitions(new EncodedResource(resource))

    // XmlBeanDefinitionReader 类中的方法
    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());
    			}
    			// 1、核心方方法,执行加载 BeanDefiniton ----(详情见代码块十一)
    			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(inputSource, encodedResource.getResource())

    // XmlBeanDefinitionReader 类中的方法
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    			throws BeanDefinitionStoreException {
    	try {
    		// 1、根据当前的一个 Spring 的配置文件创建 Document 对象
    		Document doc = doLoadDocument(inputSource, resource);
    		// 2、注册 BeanDefinition ---- (详情见代码块十二)
    		return registerBeanDefinitions(doc, resource);
    	}
    	catch (BeanDefinitionStoreException ex) {
    		throw ex;
    	}
    	catch (SAXParseException ex) {
    		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    				"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    	}
    	catch (SAXException ex) {
    		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    				"XML document from " + resource + " is invalid", ex);
    	}
    	catch (ParserConfigurationException ex) {
    		throw new BeanDefinitionStoreException(resource.getDescription(),
    				"Parser configuration exception parsing XML from " + resource, ex);
    	}
    	catch (IOException ex) {
    		throw new BeanDefinitionStoreException(resource.getDescription(),
    				"IOException parsing XML document from " + resource, ex);
    	}
    	catch (Throwable ex) {
    		throw new BeanDefinitionStoreException(resource.getDescription(),
    				"Unexpected exception parsing XML document from " + resource, ex);
    	}
    }

    代码块十二、registerBeanDefinitions(doc, resource)

    // XmlBeanDefinitionReader 类中的方法
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    	// 1、创建 BeanDefinitionDocumentReader 对象
    	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    	// 2、获取之前注册的 BeanDefinition 的个数
    	int countBefore = getRegistry().getBeanDefinitionCount();
    	// 3、注册 BeanDefinition ---- (详情见代码块十三)
    	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    	// 4、返回本次注册的 BeanDefinition 总数目
    	return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    

    代码块十三、registerBeanDefinitions(doc, createReaderContext(resource))

    // DefaultBeanDefinitionDocumentReader 类中的方法
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    	this.readerContext = readerContext;
    	logger.debug("Loading bean definitions");
    	// 1、获取 Document 元素的根节点, Spring 配置文件的根节点一般都是 beans
    	Element root = doc.getDocumentElement();
    	// 2、根据根节点注册 BeanDefinitions ---- (详情见代码块十四)
    	doRegisterBeanDefinitions(root);
    }

    代码块十四、doRegisterBeanDefinitions(root)

    protected void doRegisterBeanDefinitions(Element root) {
    	// 1、
    	BeanDefinitionParserDelegate parent = this.delegate;
    	this.delegate = createDelegate(getReaderContext(), root, parent);
    	// 1、校验 root 节点的命名空间是否为默认的命名空间(Spring 默认命名空间http://www.springframework.org/schema/beans)
    	if (this.delegate.isDefaultNamespace(root)) {
    		// 2、获取 Profile 属性
    		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    		if (StringUtils.hasText(profileSpec)) {
    			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
    					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    			// 3、校验当前节点的 profile 是否符合当前环境定义的,,如果不是则直接跳过,不解析该节点下的内容
    			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
    				if (logger.isInfoEnabled()) {
    					logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
    							"] not matching: " + getReaderContext().getResource());
    				}
    				return;
    			}
    		}
    	}
    	// 4、钩子方法,留给子类实现
    	preProcessXml(root);
    	// 5、解析 BeanDefinitions ---- (详情见代码块十五)
    	parseBeanDefinitions(root, this.delegate);
    	// 6、钩子方法,留给子类实现
    	postProcessXml(root);
    	this.delegate = parent;
    }

    profile 属性主要用于多环境开发,用于切换不同的环境,例如下图:

    我们可以在配置文件中同时写上多套配置来适用于 dev 环境、sit 环境、uat 环境,这样可以方便的进行切换开发、部署环境,最常用的就是更换不同的数据库.具体使用哪个环境在 web.xml 中通过参数 spring.profiles.active 来配置,如下图

    代码块十五、parseBeanDefinitions(root, this.delegate)

    // DefaultBeanDefinitionDocumentReader 类中的方法
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    	if (delegate.isDefaultNamespace(root)) {
    		NodeList nl = root.getChildNodes();
    		for (int i = 0; i < nl.getLength(); i++) {
    			Node node = nl.item(i);
    			if (node instanceof Element) {
    				Element ele = (Element) node;
    				if (delegate.isDefaultNamespace(ele)) {
    					// 1、解析默认名称空间下面的默认元素
    					parseDefaultElement(ele, delegate);
    				}
    				else {
    					// 2、解析默认名称空间下面的自定义元素
    					delegate.parseCustomElement(ele);
    				}
    			}
    		}
    	}
    	else {
    		// 3、解析自定义命名空间下的元素
    		delegate.parseCustomElement(root);
    	}
    }
    

    先看一张图,这里面的不带前缀的就是默认的命名空间,对应的是: xmlns="http://www.springframework.org/schema/beans ,该空间中默认的元素有 import、alias、beans、description、import

    默认空间下的自定义元素我们经常用到的主要有 <context:component-scan base-package="com.bocom"> 、<tx:annotation-driven/> 等,不过要使用这些标签需要引入对应的名称空间例如 context、tx

    自定义的命名空间就是自己定义的名称空间

    参考:https://joonwhee.blog.csdn.net/article/details/86563620

  • 相关阅读:
    关于LEA指令(单周期就可以做简单的算术计算)
    说说字符集和编码(非常形象,非常有意思)
    类依赖项的不透明性和透明性
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]
    大端和小端
    Google C++测试框架系列:入门
    随机森林中的数学基础之大数定律
    K-means Algorithm
    JavaScript插件——按钮
    分布式系统设计(8)
  • 原文地址:https://www.cnblogs.com/xiaomaomao/p/14106990.html
Copyright © 2011-2022 走看看