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的该方法。
  • 相关阅读:
    【数据结构】线性表&&顺序表详解和代码实例
    【智能算法】超详细的遗传算法(Genetic Algorithm)解析和TSP求解代码详解
    【智能算法】用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 (TSP, Traveling Salesman Problem)
    【智能算法】迭代局部搜索(Iterated Local Search, ILS)详解
    10. js时间格式转换
    2. 解决svn working copy locked问题
    1. easyui tree 初始化的两种方式
    10. js截取最后一个斜杠后面的字符串
    2. apache整合tomcat部署集群
    1. apache如何启动
  • 原文地址:https://www.cnblogs.com/huangzefeng/p/10322405.html
Copyright © 2011-2022 走看看