SpringIOC源码阅读笔记(2)

整个ioc的过程可以分为3个步骤:
1.资源定位,根据location找到对应的资源。
2.装载,将资源解析到对应的beanDefinition。
3.注册,将beanDefinition中的对象注册进容器。
给一个简单的例子,后面的分析也将从这个例子中展开
BeanFactory bf= new XmlBeanFactory(new ClassPathResource(“beanFactory.xml”))
classpathresource的定位就不细说了,

//XmlBeanFactory的主要实现都在父类DefaultListableBeanFactory中
public class XmlBeanFactory extends DefaultListableBeanFactory {
//每个BeanFactory对应有一个BeanDefinition的读取器
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
	//没有父容器的定义方法
public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

接下来进入org.springframework.beans.factory.xml.XmlBeanDefinitionReader

@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

首先进行的是EncodedResource的封装,用于对资源文件的编码进行处理的
主要是为了getReader方法的时候可以根据指定的编码或者字符集来生成字符流

public Reader getReader() throws IOException {
		if (this.charset != null) {
			return new InputStreamReader(this.resource.getInputStream(), this.charset);
		}
		else if (this.encoding != null) {
			return new InputStreamReader(this.resource.getInputStream(), this.encoding);
		}
		else {
			return new InputStreamReader(this.resource.getInputStream());
		}
	}

沿着XmlBeanDefinitionReader类继续分析,进入了真正的数据准备阶段:

//当前线程中正在加载EncodedResource集合
	private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
			new NamedThreadLocal<>("XML bean definition resources currently being loaded");
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();
		//如果为null,就设置一个大小为4的set进去
		if (currentResources == null) {
			currentResources = new HashSet<>(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 {
			//接下来会用sax来解析xml,因此先将字节流转成InputSource
				InputSource inputSource = new InputSource(inputStream);
				//设置编码
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//核心逻辑,执行加载beanDefinition
				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();
			}
		}
	}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
		//获取XML Document实例
			Document doc = doLoadDocument(inputSource, resource);
			//根据Document实例来注册Bean信息
			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);
		}
	}
private DocumentLoader documentLoader = new DefaultDocumentLoader();
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}

上述方法中有5个参数:
1.inputSource
2.getEntityResolver()
首先看一下这个EntityResolver到底是什么:
这是一个接口,并且只有一个方法,主要作用是项目本身就可以提供一个如何寻找DTD声明的办法。

//接口方法接收两个参数publicId(被引用的外部实体的公共标识符)和systemId(被引用的外部实体的系统标识符)。
public abstract InputSource resolveEntity (String publicId,
                                               String systemId)
        throws SAXException, IOException;

返回方法本身

protected EntityResolver getEntityResolver() {
		if (this.entityResolver == null) {
			// Determine default EntityResolver to use.
			ResourceLoader resourceLoader = getResourceLoader();
			if (resourceLoader != null) {
			//指定了资源定位器,就使用ResourceEntityResolver
				this.entityResolver = new ResourceEntityResolver(resourceLoader);
			}
			else {
			//如果没有指定,使用DelegatingEntityResolver
				this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
			}
		}
		return this.entityResolver;
	}

看一下上述两个类的实现:
这是ResourceEntityResolver的,父类是DelegatingEntityResolver:

private final ResourceLoader resourceLoader;

@Override
@Nullable
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException {
    // 调用父类的方法,进行解析
    InputSource source = super.resolveEntity(publicId, systemId);
    // 解析失败,resourceLoader 进行解析
    if (source == null && systemId != null) {
        // 获得 resourcePath ,即 Resource 资源地址
        String resourcePath = null;
        try {
            String decodedSystemId = URLDecoder.decode(systemId, "UTF-8"); // 使用 UTF-8 ,解码 systemId
            String givenUrl = new URL(decodedSystemId).toString(); // 转换成 URL 字符串
            // 解析文件资源的相对路径(相对于系统根路径)
            String systemRootUrl = new File("").toURI().toURL().toString();
            // Try relative to resource base if currently in system root.
            if (givenUrl.startsWith(systemRootUrl)) {
                resourcePath = givenUrl.substring(systemRootUrl.length());
            }
        } catch (Exception ex) {
            // Typically a MalformedURLException or AccessControlException.
            if (logger.isDebugEnabled()) {
                logger.debug("Could not resolve XML entity [" + systemId + "] against system root URL", ex);
            }
            // No URL (or no resolvable URL) -> try relative to resource base.
            resourcePath = systemId;
        }
        if (resourcePath != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to locate XML entity [" + systemId + "] as resource [" + resourcePath + "]");
            }
            // 获得 Resource 资源
            Resource resource = this.resourceLoader.getResource(resourcePath);
            // 创建 InputSource 对象
            source = new InputSource(resource.getInputStream());
            // 设置 publicId 和 systemId 属性
            source.setPublicId(publicId);
            source.setSystemId(systemId);
            if (logger.isDebugEnabled()) {
                logger.debug("Found XML entity [" + systemId + "]: " + resource);
            }
        }
    }
    return source;
}

这是DelegatingEntityResolver的:

@Override
	@Nullable
	public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException {
		if (systemId != null) {
			if (systemId.endsWith(DTD_SUFFIX)) {
			//如果是dtd用dtdResolver来解析
				return this.dtdResolver.resolveEntity(publicId, systemId);
			}
			//xsd用schemaResolver解析
			else if (systemId.endsWith(XSD_SUFFIX)) {
				return this.schemaResolver.resolveEntity(publicId, systemId);
			}
		}
		return null;
	}

dtdResolver对应的是BeansDtdResolver

@Override
	@Nullable
	public InputSource resolveEntity(String publicId, @Nullable String systemId) throws IOException {
		if (logger.isTraceEnabled()) {
			logger.trace("Trying to resolve XML entity with public ID [" + publicId +
					"] and system ID [" + systemId + "]");
		}
		if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
			int lastPathSeparator = systemId.lastIndexOf("/");
			int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
			//spring-beans是否存在
			if (dtdNameStart != -1) {
			//spring-beans.dtd
				String dtdFile = DTD_NAME + DTD_EXTENSION;
				if (logger.isTraceEnabled()) {
					logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
				}
				try {
					Resource resource = new ClassPathResource(dtdFile, getClass());
					InputSource source = new InputSource(resource.getInputStream());
					source.setPublicId(publicId);
					source.setSystemId(systemId);
					if (logger.isDebugEnabled()) {
						logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
					}
					return source;
				}
				catch (IOException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
					}
				}

			}
		}

		// Use the default behavior -> download from website or wherever.
		return null;
	}

schemaResolver对应的是PluggableSchemaResolver

	@Override
	@Nullable
	public InputSource resolveEntity(String publicId, @Nullable String systemId) throws IOException {
		if (logger.isTraceEnabled()) {
			logger.trace("Trying to resolve XML entity with public id [" + publicId +
					"] and system id [" + systemId + "]");
		}

		if (systemId != null) {
		//类加载器加载"META-INF/spring.schemas"
			String resourceLocation = getSchemaMappings().get(systemId);
			if (resourceLocation != null) {
				Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
				try {
					InputSource source = new InputSource(resource.getInputStream());
					source.setPublicId(publicId);
					source.setSystemId(systemId);
					if (logger.isDebugEnabled()) {
						logger.debug("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
					}
					return source;
				}
				catch (FileNotFoundException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("Couldn't find XML schema [" + systemId + "]: " + resource, ex);
					}
				}
			}
		}
		return null;
	}

3.this.errorhandler
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger)
4.getValidationModeForResource(resource)

protected int getValidationModeForResource(Resource resource) {
//默认的是XmlValidationModeDetector.VALIDATION_AUTO
		int validationModeToUse = getValidationMode();
		//不是默认的就直接返回
		if (validationModeToUse != VALIDATION_AUTO) {
			return validationModeToUse;
		}
		//检测验证模式
		int detectedMode = detectValidationMode(resource);
		if (detectedMode != VALIDATION_AUTO) {
			return detectedMode;
		}
		//都没有返回XSD验证模式
		return VALIDATION_XSD;
	}
private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
protected int detectValidationMode(Resource resource) {
//不可读,异常
		if (resource.isOpen()) {
			throw new BeanDefinitionStoreException(
					"Passed-in Resource [" + resource + "] contains an open stream: " +
					"cannot determine validation mode automatically. Either pass in a Resource " +
					"that is able to create fresh streams, or explicitly specify the validationMode " +
					"on your XmlBeanDefinitionReader instance.");
		}
		//打开stream流
		InputStream inputStream;
		try {
			inputStream = resource.getInputStream();
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
					"Did you attempt to load directly from a SAX InputSource without specifying the " +
					"validationMode on your XmlBeanDefinitionReader instance?", ex);
		}

		try {
			return this.validationModeDetector.detectValidationMode(inputStream);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
					resource + "]: an error occurred whilst reading from the InputStream.", ex);
		}
	}

接下来代码进入了org.springframework.util.xml.XmlValidationModeDetector

	public int detectValidationMode(InputStream inputStream) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		try {
		//默认非DTD,即XSD
			boolean isDtdValidated = false;
			String content;
			//循环逐行读取XML文件的内容
			while ((content = reader.readLine()) != null) {
				content = consumeCommentTokens(content);
				if (this.inComment || !StringUtils.hasText(content)) {
					continue;
				}
				//如果其中有DOCTYPE就是dtd
				if (hasDoctype(content)) {
					isDtdValidated = true;
					break;
				}
				//
				if (hasOpeningTag(content)) {
					// End of meaningful data...
					break;
				}
			}
			return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
		}
		catch (CharConversionException ex) {
			return VALIDATION_AUTO;
		}
		finally {
			reader.close();
		}
	}

5.isNamespaceAware()
指示此解析器是否被配置为可识别名称空间。
默认为false

接下来分析loadDocument方法,该方法的实现是在org.springframework.beans.factory.xml.DefaultDocumentLoader。

	@Override
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
		//创建DocumentBuilderFactory
		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		//创建 DocumentBuilder
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		//解析XML InputSource 返回 Document 对象
		return builder.parse(inputSource);
	}
private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
	protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {
		//创建DocumentBuilderFactory
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		//设置命名空间支持
		factory.setNamespaceAware(namespaceAware);
		//验证方式不为none就开启验证
		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);
			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
			// XSD 模式下,强制设置命名空间支持
				factory.setNamespaceAware(true);
				try {
				// 设置 SCHEMA_LANGUAGE_ATTRIBUTE
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					ParserConfigurationException pcex = new ParserConfigurationException(
							"Unable to validate using XSD: Your JAXP provider [" + factory +
							"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
							"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
					pcex.initCause(ex);
					throw pcex;
				}
			}
		}

		return factory;
	}
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
			@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
			throws ParserConfigurationException {
		// 创建 DocumentBuilder 对象
		DocumentBuilder docBuilder = factory.newDocumentBuilder();
		// 设置 EntityResolver 属性
		if (entityResolver != null) {
			docBuilder.setEntityResolver(entityResolver);
		}
		// 设置 ErrorHandler 属性
		if (errorHandler != null) {
			docBuilder.setErrorHandler(errorHandler);
		}
		return docBuilder;
	}

接下来回到XmlBeanDefinitionReader的registerBeanDefinitions方法:
通过上面的分析我们已经将xml文件资源转换成了解析完的Document类型

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //创建BeanDefinitionDocumentReader 对象
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//获取已注册的 BeanDefinition 数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//创建XmlReaderContext对象,注册BeanDefinition
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//计算新注册的 BeanDefinition 数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

首先是createBeanDefinitionDocumentReader

private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
//生成DefaultBeanDefinitionDocumentReader
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	}

getRegistry()返回的是AbstractBeanDefinitionReader中的registry
而我们一直说的DefaultListableBeanFactory就是实现了BeanDefinitionRegistry,换句话说就是个注册中心,那么
getBeanDefinitionCount返回的其实就是注册中心中注册的那些beandefinition的数量。

private ProblemReporter problemReporter = new FailFastProblemReporter();

private ReaderEventListener eventListener = new EmptyReaderEventListener();

private SourceExtractor sourceExtractor = new NullSourceExtractor();
public XmlReaderContext createReaderContext(Resource resource) {
//创建 XmlReaderContext 对象
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}

接下来是registerBeanDefinitions,实现在DefaultBeanDefinitionDocumentReader

@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//获取XML的Root Element
		Element root = doc.getDocumentElement();
		//执行注册BeanDefinition
		doRegisterBeanDefinitions(root);
	}
protected void doRegisterBeanDefinitions(Element root) {
// 记录老的 BeanDefinitionParserDelegate 对象
		BeanDefinitionParserDelegate parent = this.delegate;
		//创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
		this.delegate = createDelegate(getReaderContext(), root, parent);
		//检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans
		if (this.delegate.isDefaultNamespace(root)) {
		//处理 profile 属性
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
			//使用分隔符切分,可能有多个 profile
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
						//如果所有 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;
				}
			}
		}
		//解析前处理
		preProcessXml(root);
		//解析
		parseBeanDefinitions(root, this.delegate);
		//解析后处理
		postProcessXml(root);
		//将delegate恢复
		this.delegate = parent;
	}
protected BeanDefinitionParserDelegate createDelegate(
			XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
		// 创建 BeanDefinitionParserDelegate 对象
		BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
		 // 初始化默认
		delegate.initDefaults(root, parentDelegate);
		return delegate;
	}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//根节点是默认命名空间(beans,alias,import,bean),执行默认解析
		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)) {
						parseDefaultElement(ele, delegate);
					}
					else {
					// 如果该节点非默认命名空间,执行自定义解析
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
		//如果根节点非默认命名空间,执行自定义解析
			delegate.parseCustomElement(root);
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值