第5讲:XmlBeanDefinitionReader与Resource

观察defaultlistablebeanfactory的属性

分析ClasspathResource的创建

分析DefaultListableBeanFactory的创建

 

1、classPathResource的构造方法

2、分析DefaultListableBeanFactory的创建

3、XmlBeanDefinitinRead的创建

 

1、classPathResource内部结构很简单,有两个重要属性:

String path:存储资源路径的地址

ClassLoader classLoader:类加载器(负责将配置文件(磁盘)加载到jvm中(内存),当然也负责类文件加载到jvm中)

所以Resource类并没有做什么实际性的操作,只是定位资源信息而已

2、DefaultListableBeanFactory(IOC容器)

就是平时我们所说的Ioc容器,它里面定义了很多属性:

我最关心的一个属性:

Map<String, BeanDefinition> beanDefinitionMap:存储bean定义信息

其他几个次要关心的属性:

Map<Class<?>, String[]> allBeanNamesByType: 存储bean名称和bean类型的关联关系

Map<Class<?>, String[]> singletonBeanNamesByType: 存储单例bean名称和单例bean类型的关联关系

List<String> beanDefinitionNames: 存储所有bean定义信息的名称

说实话,上述这几个属性不用可以去记,后面章节用到了你就知道了,这么多属性记住主要的就行了

所以说这个类本身就代表了一个容器,里面有对这些属性的一系列操作(crud),可以大胆猜测平时我们获取bean应该就是直接跟这个类打交道,它就是IOC

 

3、XmlBeanDefinitinRead

这个类好像也是什么都没干,只是定义了一些属性而已,当然创建它的时候有会把BeanFactory当作BeanDefinitionRegistry属性。因为BeanFactory实现了BeanDefinitionRegistry接口。

 

4、reader.loadBeanDefinitions(resource)

这个方法才在真正解析bean定义,上面的3个步骤都是在做一些准备工作。

/**
	 * Load bean definitions from the specified XML file.
	 * @param encodedResource the resource descriptor for the XML file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	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());
		}
            //从ThreadLocal中获取当前线程的Set<EncodedResource>  如需详细了解ThreadLocal,链接:https://www.jianshu.com/p/3c5d7f09dfbd
		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 {
              //获取resource的输入流 详解见代码块1
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
                       //把inputstream包装成一个InputSource 
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
                        //真正加载beanDefinitions开始了 详解见代码块2
				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();
			}
		}
	}

 

代码块1:encodedResource.getResource().getInputStream()

/**
	 * This implementation opens an InputStream for the given class path resource.
	 * @see java.lang.ClassLoader#getResourceAsStream(String)
	 * @see java.lang.Class#getResourceAsStream(String)
	 */
    //这个方法来自于Resource的一个子类:ClassPathResource
	@Override
	public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
                  //clazz是要给Class对象,通过Class对象将一个路径资源直接转换成要给输入流(详解见:内部代码块1)
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}
 
   内部代码块1:
   public InputStream getResourceAsStream(String name) {
       //通过下面的代码可以看到,其实还是通过调用类加载器的方法,实现把一个资源转换成一个输入流的
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name); //(详解见:内部代码块2)
    }
    
    内部代码块2:
    public InputStream getResourceAsStream(String name) {
        //可以看到ClassLoader又是通过URL对象来实现对资源的解析的
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;(详解见:内部代码块3)
        } catch (IOException e) {
            return null;
        }
    }   
    
    内部代码块3:
    public final InputStream openStream() throws java.io.IOException {
        //更新的代码就不看了,我这里猜想内部实现细节应该是会通过创建InputStream来获取到资源输入流
        return openConnection().getInputStream();
    }

代码块2:doLoadBeanDefinitions(inputSource, encodedResource.getResource())

 

/**
	 * Actually load bean definitions from the specified XML file.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
                  //获取到Document对象(详解见:代码块3)
			Document doc = doLoadDocument(inputSource, resource);
                 //获取到Document对象(详解见:代码块4)
			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);
		}
	}

代码块3:doLoadDocument(inputSource, resource)

 /**
	 * Actually load the specified document using the configured DocumentLoader.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the DOM Document
	 * @throws Exception when thrown from the DocumentLoader
	 * @see #setDocumentLoader
	 * @see DocumentLoader#loadDocument
	 */
   //通过DocumentLoader加载Document对象,具体细节不深入研究
	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}

 

代码块4:registerBeanDefinitions(doc, resource)

 

/**
	 * Register the bean definitions contained in the given DOM document.
	 * Called by {@code loadBeanDefinitions}.
	 * <p>Creates a new instance of the parser class and invokes
	 * {@code registerBeanDefinitions} on it.
	 * @param doc the DOM document
	 * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
	 */
   //该方法的返回值是beanDefinitin的个数(感觉没卵用)
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//获取BeanDefinitionDocumentReader对象,用于真正解析Document对象  (详解见:内部代码块1)
           BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
           //获取Document中BeanDefinition的个数
		int countBefore = getRegistry().getBeanDefinitionCount();
           //真正解析BeanDefinition (详解见:第六讲)
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 
           //注册BeanDefinition的数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
 
   内部代码块1:
   /**
	 * Create the {@link BeanDefinitionDocumentReader} to use for actually
	 * reading bean definitions from an XML document.
	 * <p>The default implementation instantiates the specified "documentReaderClass".
	 * @see #setDocumentReaderClass
	 */
   //该方法会返回要给BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader (该类的介绍详解见:内部代码块2)
	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	} 
   
   内部代码块2:
   //BeanDefinitionDocumentReader 该类是DefaultBeanDefinitionDocumentReader 的默认实现类,该类中定义了XML文档的结构、元素和属性名,
   //也就是说xml中出现的元素名称、元素的属性名都定义在了这个类的属性中,如:
   //String NESTED_BEANS_ELEMENT = "beans"; String ALIAS_ELEMENT = "alias"; String NAME_ATTRIBUTE = "name";String ALIAS_ATTRIBUTE = "alias";IMPORT_ELEMENT = "import";RESOURCE_ATTRIBUTE = "resource";PROFILE_ATTRIBUTE = "profile";
   //更多的名称可在 BeanDefinitionParserDelegate类中查看,为什么我会突然提到这个类呢?因为DefaultBeanDefinitionDocumentReader类中确实有引用到这个类
   //BeanDefinitionDocumentReader 类中有一个重要的属性:BeanDefinitionParserDelegate,它就是真正用于解析BeanDefinition的,是解析BeanDefinition的一个代理 (详解见:第六讲)

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值