spring源码解析---spring-core(一)

 
 
使用版本spring4.2.2或者4.1.1
 

基本

本部分从最基本的Spring开始。配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean class="base.SimpleBean"></bean>
</beans>
###原作者没有写beans的命名空间及约束 导致xml验证失败,这里加上了
###bean部分就是类的引用地址
启动代码:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
SimpleBean bean = context.getBean(SimpleBean.class);
bean.send();
context.close();
}
SimpleBean:
public class SimpleBean {
public void send() {
System.out.println("I am send method from SimpleBean!");
}
}
 

ClassPathXmlApplicationContext

整个继承体系如下:
 
spring源码解析---spring-core(一)
ResourceLoader代表了加载资源的一种方式,正是策略模式的实现
 
构造器源码:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
//null
super(parent);
setConfigLocations(configLocations);
//默认true
if (refresh) {
refresh();
}
}spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
 
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); 调用只会是上述两个方法其中一个,这里明显是ClassPathXmlApplicationContext(String configLocation)
后面实际调用的都是本类的这个方法,如下图:
spring源码解析---spring-core(一)
 
其实ClassPathXmlApplicationContext类里面的构造器(包括无参的共有9个在这个版本)。
 
 
 

构造器

 
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
。。。。。。此处省略几个向上的父类,一直到底抽象类 AbstractApplicationContext
首先看父类构造器,沿着继承体系一直向上调用,直到AbstractApplicationContext:
 
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
public AbstractApplicationContext(ApplicationContext parent) {
    this();
    setParent(parent);
}
public AbstractApplicationContext() {
    this.resourcePatternResolver = getResourcePatternResolver();
}
 
PathMatchingResourcePatternResolver支持Ant风格的路径解析。下面简单概述下ant风格路径.
spring源码解析---spring-core(一)
 
 

设置配置文件路径

AbstractRefreshableConfigApplicationContext.setConfigLocations:
public void setConfigLocations(String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
 
resolvePath:
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
此方法的目的在于将占位符(placeholder)解析成实际的地址。比如可以这么写: new ClassPathXmlApplicationContext("classpath:config.xml");那么classpath:就是需要被解析的。
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
getEnvironment方法来自于ConfigurableApplicationContext接口,源码很简单,如果为空就调用createEnvironment创建一个。AbstractApplicationContext.createEnvironment:
protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();//看下面的继承关系可以知道StandardEnvironment是ConfigurableEnvironment的实现类
}
spring源码解析---spring-core(一)
 
 

Environment接口

继承体系:
spring源码解析---spring-core(一)
Environmen接口**代表了当前应用所处的环境。**从此接口的方法可以看出,其主要和profile、Property相关。

Profile

Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。**Spring容器管理的所有bean都是和一个profile绑定在一起的。**使用了Profile的配置文件示例:
 
spring源码解析---spring-core(一)
 
在启动代码中可以用如下代码设置活跃(当前使用的)Profile:
context.getEnvironment().setActiveProfiles("dev");
当然使用的方式还有很多(比如注解),参考:

Property

这里的Property指的是程序运行时的一些参数,引用注释:
properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.

Environment实现类 AbstractEnvironment 构造器

private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
spring源码解析---spring-core(一)

PropertySources接口

继承体系:
spring源码解析---spring-core(一)spring源码解析---spring-core(一)
 
 
此接口实际上是PropertySource的容器,默认的MutablePropertySources实现内部含有一个CopyOnWriteArrayList作为存储载体。
StandardEnvironment.customizePropertySources:
/** System environment property source name: {@value} */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource
(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource
(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

PropertySource接口

PropertySource接口代表了键值对的Property来源。继承体系:
 
 
spring源码解析---spring-core(一)
 
AbstractEnvironment.getSystemProperties:(此方法在org.springframework.core.env.AbstractEnvironment里面
spring源码解析---spring-core(一)
 
@Overridepublic Map<String, Object> getSystemProperties() {
try {
return (Map) System.getProperties();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info(format("Caught AccessControlException when accessing system " +
"property [%s]; its value will be returned [null]. Reason: %s",
attributeName, ex.getMessage()));
}
return null;
}
}
};
}
}
这里的实现很有意思,如果安全管理器阻止获取全部的系统属性,那么会尝试获取单个属性的可能性,如果还不行就抛异常了。
getSystemEnvironment方法也是一个套路,不过最终调用的是System.getenv,可以获取jvm和OS的一些版本信息。

路径Placeholder处理

AbstractEnvironment.resolveRequiredPlaceholders:
 
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
//text即配置文件路径,比如classpath:config.xml
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
propertyResolver是一个PropertySourcesPropertyResolver对象:
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);

PropertyResolver接口

PropertyResolver继承体系(排除Environment分支):
 

路径Placeholder处理

AbstractEnvironment.resolveRequiredPlaceholders:
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
//text即配置文件路径,比如classpath:config.xml
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
propertyResolver是一个PropertySourcesPropertyResolver对象:
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);

PropertyResolver接口

PropertyResolver继承体系(排除Environment分支):
 
spring源码解析---spring-core(一)
 
此接口正是用来解析PropertyResource。

解析

AbstractPropertyResolver.resolveRequiredPlaceholders:
 
 
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
 
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
//三个参数分别是${, }, :
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
doResolvePlaceholders:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
//PlaceholderResolver接口依然是策略模式的体现
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
    return getPropertyAsRawString(placeholderName);
}
});
}
其实代码执行到这里的时候还没有进行xml配置文件的解析,那么这里的解析placeHolder是什么意思呢,原因在于可以这么写:
System.setProperty("spring", "classpath");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("${spring}:config.xml");
SimpleBean bean = context.getBean(SimpleBean.class);
这样就可以正确解析。placeholder的替换其实就是字符串操作,这里只说一下正确的属性是怎么来的。实现的关键在于PropertySourcesPropertyResolver.getProperty:
 
@Overrideprotected String getPropertyAsRawString(String key) {
    return getProperty(key, String.class, false);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        for (PropertySource<?> propertySource : this.propertySources) {
            Object value = propertySource.getProperty(key);
            return value;
        }
}
return null;
}
 
很明显了,就是从System.getProperty和System.getenv获取,但是由于环境变量是无法自定义的,所以其实此处只能通过System.setProperty指定。
注意,classpath:XXX这种写法的classpath前缀到目前为止还没有被处理。

refresh

spring源码解析---spring-core(一)
super(parent)和this.setConfigLocations(configLocations);说完 下面说是refresh这个方法
 
Spring bean解析就在此方法,所以单独提出来。
AbstractApplicationContext.refresh:
spring源码解析---spring-core(一)
作者版本:
@Overridepublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

prepareRefresh

protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
// Initialize any placeholder property sources in the context environment
//空实现
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
 

属性校验

AbstractEnvironment.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() throws MissingRequiredPropertiesException {
    this.propertyResolver.validateRequiredProperties();
}
AbstractPropertyResolver.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
    if (this.getProperty(key) == null) {
        ex.addMissingRequiredProperty(key);
      }
    }
    if (ex.getMissingRequiredProperties().isEmpty()) {
        throw ex;
    }
}
spring源码解析---spring-core(一)
requiredProperties是通过setRequiredProperties方法设置的,保存在一个set里面,默认是空的,也就是不需要校验任何属性。
spring源码解析---spring-core(一)作者说是list集合里,实际是错的,当然也许是版本不同而已。
 
 
 
 
BeanFactory创建
      在refresh的这个方法里面
         ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//下面会讲到这个
         this.prepareBeanFactory(beanFactory);
 
由obtainFreshBeanFactory调用AbstractRefreshableApplicationContext.refreshBeanFactory:(见下图)
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
(这部分与作者源码一致)
 

BeanFactory接口

此接口实际上就是Bean容器,其继承体系:
 
spring源码解析---spring-core(一)

BeanFactory定制

AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
//默认false,不允许覆盖
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
//默认false,不允许循环引用
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}

Bean加载

AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:
@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 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));// 为Bean读取器设置SAX xml解析器,下面会说到这个 
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//默认空实现
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}

EntityResolver

此处只说明用到的部分继承体系:
spring源码解析---spring-core(一)spring源码解析---spring-core(一)
 
EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析。
 

BeanDefinitionReader

继承体系:
 
spring源码解析---spring-core(一)
 
spring源码解析---spring-core(一)

路径解析(Ant)

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
//here
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader.loadBeanDefinitions:
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
下面作者这个肯定是不对的,因为调用了方法一样但参数列表不一样,实际是重载的loadBeanDefinitions方法 ,上图两个是4.1.1的方法内容,与作者的版本不同。故贴出来。 
4.1.1版本与这个稍微有些不同
@Overridepublic int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
 
spring源码解析---spring-core(一)
之后调用:
//第二个参数为空
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = this.getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    } else {
        int loadCount;
        if (!(resourceLoader instanceof ResourcePatternResolver)) {
            Resource resource = resourceLoader.getResource(location);
            loadCount = this.loadBeanDefinitions((Resource)resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
 
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
 
            return loadCount;
        } else {
            try {
                Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                loadCount = this.loadBeanDefinitions(resources);
                if (actualResources != null) {
                    Resource[] var6 = resources;
                    int var7 = resources.length;
 
                    for(int var8 = 0; var8 < var7; ++var8) {
                        Resource resource = var6[var8];
                        actualResources.add(resource);
                    }
                }
 
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
 
                return loadCount;
            } catch (IOException var10) {
                throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
            }
        }
    }
}
 
getResources(作者写成getResource,一字之差缪之千里),在AbstractApplicationContext.getResources
@Override
public Resource[] getResources(String locationPattern) throws IOException {
//构造器中初始化,PathMatchingResourcePatternResolver对象
return this.resourcePatternResolver.getResources(locationPattern);
}
 
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
进入第二个方法,因为第一个是掉本类的方法。
public Resource[] getResources(String locationPattern) throws IOException {
//如果是ResourcePatternResolver
return this.resourceLoader instanceof ResourcePatternResolver ? ((ResourcePatternResolver)this.resourceLoader).getResources(locationPattern) : super.getResources(locationPattern);
}
PathMatchingResourcePatternResolver是ResourceLoader继承体系的一部分。
public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith("classpath*:")) {
    //matcher是一个AntPathMatcher对象
        return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) :                                                       this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
} else {
       int prefixEnd = locationPattern.indexOf(":") + 1;
       return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]
      {this.getResourceLoader().getResource(locationPattern)};
    }
}
isPattern:
spring源码解析---spring-core(一)
注:本来以为是数字 ,后面才发现ASCII代表的数字:详情可查https://baike.baidu.com/item/ASCII/309296?fr=aladdin
spring源码解析---spring-core(一)十进制指的是42
spring源码解析---spring-core(一)
也经过测试发现是根据十进制的ASCII码参与运算的。
spring源码解析---spring-core(一)
 
 
spring源码解析---spring-core(一)
  也就是可以这么写:
new ClassPathXmlApplicationContext("con*.xml");
更多的例子
spring源码解析---spring-core(一)
具体怎么解析ant风格的就不写了。
 

配置文件加载

入口方法在AbstractBeanDefinitionReader:
spring源码解析---spring-core(一)
//加载Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//解析int loadCount = loadBeanDefinitions(resources);
最终逐个调用XmlBeanDefinitionReader的loadBeanDefinitions方法:
spring源码解析---spring-core(一)
Resource是代表一种资源的接口,其类图:
 
spring源码解析---spring-core(一)
 
 
EncodedResource扮演的其实是一个装饰器的模式,为InputStreamSource添加了字符编码(虽然默认为null)。这样为我们自定义xml配置文件的编码方式提供了机会。
之后关键的源码只有两三行:(这个是4.1.1版本中的源码,关键部分为标红的地方)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (this.logger.isInfoEnabled()) {
    this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }
 
    Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
    currentResources = new HashSet(4);
    this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
 
    if (!((Set)currentResources).add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
        int var5;
    try {
        InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
        InputSource inputSource = new InputSource(inputStream);
    if (encodedResource.getEncoding() != null) {
        inputSource.setEncoding(encodedResource.getEncoding());
    }
 
       var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    } finally {
       inputStream.close();
        }
    } catch (IOException var15) {
        throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
    } finally {
        ((Set)currentResources).remove(encodedResource);
    if (((Set)currentResources).isEmpty()) {
        this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
        return var5;
    }
}
spring源码解析---spring-core(一)其实也差不多。
InputSource是org.xml.sax的类。
doLoadBeanDefinitions:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
Document doc = doLoadDocument(inputSource, resource);//下面先讲讲这部分 $doLoadDocument$ 加载document
return registerBeanDefinitions(doc, resource);//在说说这部分$registerBeanDefinitions$  主要是注册bean
}(4.1.1版本有些不同,主要在抛出异常方面)
($doLoadDocument$)doLoadDocument:
protected Document doLoadDocument(InputSource inputSource, Resource resource) {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
//loadDocument方法实际调用的是DefaultDocumentLoader里面loadDocument方法 #DefaultDocumentLoader.loadDocument
}
spring源码解析---spring-core(一)
实际这个documentLoader就是DefaultDocumentLoader. 此类是DocumentLoader接口的唯一实现。getEntityResolver方法返回ResourceEntityResolver,上面说过了。errorHandler是一个SimpleSaxErrorHandler对象。
 
spring源码解析---spring-core(一)
spring源码解析---spring-core(一)
校验模型其实就是确定xml文件使用xsd方式还是dtd方式来校验,忘了的话左转度娘。Spring会通过读取xml文件的方式判断应该采用哪种。
NamespaceAware默认false,因为默认配置了校验为true。(其实这个默认校验为true我还没找到)
spring源码解析---spring-core(一)
 
DefaultDocumentLoader.loadDocument:
@Overridepublic Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) {
//这里就是老套路了,可以看出,Spring还是使用了dom的方式解析,即一次全部load到内存
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
 
createDocumentBuilderFactory比较有意思:
spring源码解析---spring-core(一)
如上图所示,4.1.1版本的是这样,我跟确信下面的版本更成熟,因为很多地方是常量控制,4.1.1都是写死的常量。
 
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware{
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        //此方法设为true仅对dtd有效,xsd(schema)无效
        factory.setValidating(true);
    if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // Enforce namespace aware for XSD...
            //开启xsd(schema)支持
        factory.setNamespaceAware(true);
        //这个也是Java支持Schema的套路,可以问度娘
        factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
    }
}
return factory;
}
 

 

 Bean解析(这部分放在 spring源码解析---spring-core(二)