整个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);
}
}