zoukankan      html  css  js  c++  java
  • BeanDefinition的定位

    概念

    BeanDefinition的存在形式有很多种,例如文件系统中的Bean定义文件或类路径中的Bean定义文件。这就意味需要不同的寻址方式在找到这些Bean定义文件。Resource定位指的是BeanDefinition的资源定位,就是找到这些Bean定义文件,并将这些信息抽象为统一的Resource对象,方便后面载入。

    分析定位过程

    以编程的方式使用DefaultListableBeanFactory时,我们会手动定义一个Resource来定位容器使用BeanDefinition:ClassPathResource res = new ``ClassPathResource("bean.xml");然后定义一个特定的读取器来读取这个资源,因为DefaultListableBeanFactory是一个纯粹的IoC容器,并没有自带帮我们定位资源和读取资源的功能,所以需要手动为其配置特定的读取器。所以这里就能看出使用ApplicationContext的优势所在,因为在ApplicationCo-ntext中,已经为我们提供了一系列加载不同Resource的读取器的实现,例如FileSystemXmlApplicationContext,ClassPathXmlApplicationContext以及XmlWebApplicationContext等,简单地从这些类的名字上分析,可以清楚地看到它们可以提供哪些不同的Resource读入功能。当然,使用DefaultListable-BeanFactory这种更底层的容器,能提高定制IoC容器的灵活性。下面分析FileSystemXmlApplicationContext的资源定位过程。

    在上面的分析中,我们可以了解到FileSystemXmlApplicationContext的基本功能都在其基类中完成,它只是在构造函数中调用基类的refresh()来实例化容器和定义getResourceByPath()来定位BeanDefinition资源。所以可以知到getResourceByPath()是该容器定位资源调用的最后的方法,查看该方法的调用栈,便可了解整个定位过程。
    image.png

    分析调用栈:

    • FileSystemXmlApplicationContext构造器中的refresh()方法是开始容器初始化的入口方法,由refresh()来启动整个调用。
    • 在FileSystemXmlApplicationContext的基类AbstractRefreshableApplicationContext中的refreshBeanFactory()进行容器的初始化。从这里可知实际使用的BeanFactory是DefaultListableBeanFactory。代码如下:
    /**
    	 * This implementation performs an actual refresh of this context's underlying
    	 * bean factory, shutting down the previous bean factory (if any) and
    	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
    	 */
    	@Override
    	protected final void refreshBeanFactory() throws BeansException {
        //这里判断如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
    		if (hasBeanFactory()) {
    			destroyBeans();
    			closeBeanFactory();
    		}
        /**
          *创建实际存储BeanDefinition的DefaultListableBeanFactory,
          *并调用loadBeanDefinitions(beanFactory)载入BeanDefinition信息
          */ 
    		try {
    			DefaultListableBeanFactory beanFactory = createBeanFactory();
    			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);
    		}
    	}
    
      protected DefaultListableBeanFactory createBeanFactory() {
    		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    	}
    
    • loadBeanDefinitions(DefaultListableBeanFactory beanFactory),这个方法在基类AbstractRefreshableApp-licationContext中定义为抽象函数,因为允许有不同的载入方式,这里通过一个抽象函数把具体实现委托给子类,见图一和图二。FileSystemXmlApplicationContext是用xml方式读取的,所以基类AbstractXmlApplication-Context实现了这个方法。在这个方法中将DefaultListableBeanFactory绑定给BeanDefinitionReader,用于后续定位、载入和注册的回调。还需要注意的是beanDefinitionReader.setResourceLoader(this)这个方法,ResourceLoader的主要功能是用来定位和获取Resource对象的,而在Spring中每个ApplicationContext都是DefaultResourceLoader的子类(见图四),所以FileSystemXmlApplicationContext中的getResourceByPath(S-tring path)是重写父类的方法。BeanDefinitionReader绑定完需要后续回调的组件后,就由BeanDefinitionR-eader继续后续处理,见图三。
       ```java
      /**
      • Load bean definitions into the given bean factory, typically through
      • delegating to one or more bean definition readers.
      • @param beanFactory the bean factory to load bean definitions into
      • @throws BeansException if parsing of the bean definitions failed
      • @throws IOException if loading of bean definition files failed
      • @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
      • @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
        */
        //AbstractRefreshableApplicationContext中的抽象方法。
        protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
        throws BeansException, IOException;
    ![image.png](https://cdn.nlark.com/yuque/0/2019/png/172508/1548333904421-a0164d54-0bf6-4d32-97da-737c58b22ce4.png#align=left&display=inline&height=258&linkTarget=_blank&name=image.png&originHeight=258&originWidth=758&size=41985&width=758)
    ```java
    /**
    	 * Loads the bean definitions via an XmlBeanDefinitionReader.
    	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
    	 * @see #initBeanDefinitionReader
    	 * @see #loadBeanDefinitions
    	 */
    	@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);
    		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);
    	}
    

    image.png

    • loadBeanDefinitions(XmlBeanDefinitionReader reader)方法:对配置文件路径进行判断,如果路径不为空,则根据路径进行定位。
    /**
    	 * Load the bean definitions with the given XmlBeanDefinitionReader.
    	 * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
    	 * method; hence this method is just supposed to load and/or register bean definitions.
    	 * @param reader the XmlBeanDefinitionReader to use
    	 * @throws BeansException in case of bean registration errors
    	 * @throws IOException if the required XML document isn't found
    	 * @see #refreshBeanFactory
    	 * @see #getConfigLocations
    	 * @see #getResources
    	 * @see #getResourcePatternResolver
    	 */
    	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    		Resource[] configResources = getConfigResources();
    		//对configResources进行载入解析和注册
        if (configResources != null) {
    			reader.loadBeanDefinitions(configResources);
    		}
    		String[] configLocations = getConfigLocations();
        //根据configLocations定位Resource
    		if (configLocations != null) {
    			reader.loadBeanDefinitions(configLocations);
    		}
    	}
    
    • loadBeanDefinitions(String location, Set actualResources)方法:根据路径定位。
    /**
    	 * Load bean definitions from the specified resource location.
    	 * <p>The location can also be a location pattern, provided that the
    	 * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
    	 * @param location the resource location, to be loaded with the ResourceLoader
    	 * (or ResourcePatternResolver) of this bean definition reader
    	 * @param actualResources a Set to be filled with the actual Resource objects
    	 * that have been resolved during the loading process. May be <code>null</code>
    	 * to indicate that the caller is not interested in those Resource objects.
    	 * @return the number of bean definitions found
    	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
    	 * @see #getResourceLoader()
    	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
    	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
    	 */
    	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    		//这里取得ResourceLoader,就是之前绑定的FileSystemXmlApplicationContext
        ResourceLoader resourceLoader = getResourceLoader();
    		if (resourceLoader == null) {
    			throw new BeanDefinitionStoreException(
    					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    		}
        //这里对Resource的路径模式进行解析。
    		if (resourceLoader instanceof ResourcePatternResolver) {
    			// Resource pattern matching available.
    			try {
            //调用DefaultResourceLoader的getResource完成具体的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.
          //调用DefaultResourceLoader的getResource完成具体的Resource定位
    			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;
    		}
    	}
    
    • getResource(String location)方法:DefaultResourceLoader具体的Resource定位方法。
    public Resource getResource(String location) {
    		Assert.notNull(location, "Location must not be null");
      //这里处理带有classpath标识的Resource
    		if (location.startsWith(CLASSPATH_URL_PREFIX)) {
    			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    		}
    		else {
    			try {
    				// Try to parse the location as a URL...
            //这里出URL标识的Resource定位
    				URL url = new URL(location);
    				return new UrlResource(url);
    			}
    			catch (MalformedURLException ex) {
    				// No URL -> resolve as resource path.
            //如果既不是classpath.也不是URL标识的Resource定位,则把getResource的
            //重任交给getResourceByPath,这个方法是一个protected方法.默认的实现是得到
            //一个ClassPathContextResource,这个方法常常会用子类来实现
    				return getResourceByPath(location);
    			}
    		}
    	}
      
      //getResourceByPath的默认实现
      protected Resource getResourceByPath(String path) {
    		return new ClassPathContextResource(path, getClassLoader());
    	}
    
    • 上述代码的getResourceByPath(location);在这里就调用子类FileSystemXmlApplicationContext的该方法。
  • 相关阅读:
    谈To B产品路径逻辑:To B产品的核心本质到底是什么?
    做产品应该遵循哪些宏观产品原则?
    做产品应该遵循哪些微观产品原则?
    B端产品的第一性原理思考
    美团点评王慧文:互联网有AB面,最惨烈战争发生在B2
    《罗振宇 知识就是力量》
    生活是一只龙虾
    CAD-美的热水器F50-32DQ
    CAD-椭圆和圆弧命令
    CAD-圆命令
  • 原文地址:https://www.cnblogs.com/huangzefeng/p/10322405.html
Copyright © 2011-2022 走看看