美文网首页java编程/提升
spring源码解析---spring-core(一)

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

作者: 程序员ken | 来源:发表于2020-07-25 21:25 被阅读0次

    https://github.com/seaswalker

    https://github.com/seaswalker/Spring

    使用版本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

    整个继承体系如下:

    ResourceLoader代表了加载资源的一种方式,正是策略模式的实现

    构造器源码:

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {

    //null

    super(parent);

    setConfigLocations(configLocations);

    //默认true

    if (refresh) {

    refresh();

    }

    }

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); 调用只会是上述两个方法其中一个,这里明显是ClassPathXmlApplicationContext(String configLocation)

    后面实际调用的都是本类的这个方法,如下图:

    其实ClassPathXmlApplicationContext类里面的构造器(包括无参的共有9个在这个版本)。

    构造器

    。。。。。。此处省略几个向上的父类,一直到底抽象类 AbstractApplicationContext

    首先看父类构造器,沿着继承体系一直向上调用,直到AbstractApplicationContext:

    public AbstractApplicationContext(ApplicationContext parent) {

    this();
    
    setParent(parent);
    

    }

    public AbstractApplicationContext() {

    this.resourcePatternResolver \= getResourcePatternResolver();
    

    }

    PathMatchingResourcePatternResolver支持Ant风格的路径解析。下面简单概述下ant风格路径.

    设置配置文件路径

    即****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:就是需要被解析的。

    getEnvironment方法来自于ConfigurableApplicationContext接口,源码很简单,如果为空就调用createEnvironment创建一个。AbstractApplicationContext.createEnvironment:

    protected ConfigurableEnvironment createEnvironment() {

    return new StandardEnvironment();//看下面的继承关系可以知道StandardEnvironment是ConfigurableEnvironment的实现类
    

    }

    Environment接口

    继承体系:

    Environmen接口**代表了当前应用所处的环境。**从此接口的方法可以看出,其主要和profile、Property相关。

    Profile

    Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。**Spring容器管理的所有bean都是和一个profile绑定在一起的。**使用了Profile的配置文件示例:

    在启动代码中可以用如下代码设置活跃(当前使用的)Profile:

    context.getEnvironment().setActiveProfiles("dev");

    当然使用的方式还有很多(比如注解),参考:

    spring3.1 profile 配置不同的环境(打开失败)

    Spring Profiles example

    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);

    }

    PropertySources接口

    继承体系:

    此接口实际上是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来源。继承体系:

    AbstractEnvironment.getSystemProperties:(此方法在org.springframework.core.env.AbstractEnvironment里面)

    image

    @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分支):

    image

    此接口正是用来解析PropertyResource。

    解析

    AbstractPropertyResolver.resolveRequiredPlaceholders:

    @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

    super(parent)和this.setConfigLocations(configLocations);说完 下面说是refresh这个方法

    Spring bean解析就在此方法,所以单独提出来。

    AbstractApplicationContext.refresh:

    作者版本:

    @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;
    
    }
    

    }

    requiredProperties是通过setRequiredProperties方法设置的,保存在一个set里面,默认是空的,也就是不需要校验任何属性。

    作者说是list集合里,实际是错的,当然也许是版本不同而已。

    BeanFactory创建

      在refresh的这个方法里面
    
         ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//下面会讲到这个
    
         this.prepareBeanFactory(beanFactory);
    

    由obtainFreshBeanFactory调用AbstractRefreshableApplicationContext.refreshBeanFactory:(见下图)

    (这部分与作者源码一致)

    BeanFactory接口

    此接口实际上就是Bean容器,其继承体系:

    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

    此处只说明用到的部分继承体系:

    EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析。

    BeanDefinitionReader

    继承体系:

    路径解析(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:

    下面作者这个肯定是不对的,因为调用了方法一样但参数列表不一样,实际是重载的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;

    }

    之后调用:

    //第二个参数为空

    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);

    }

    进入第二个方法,因为第一个是掉本类的方法。

    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:

    注:本来以为是数字 ,后面才发现ASCII代表的数字:详情可查https://baike.baidu.com/item/ASCII/309296?fr=aladdin

    十进制指的是42

    也经过测试发现是根据十进制的ASCII码参与运算的。

    也就是可以这么写:

    new ClassPathXmlApplicationContext("con*.xml");

    更多的例子

    具体怎么解析ant风格的就不写了。

    配置文件加载

    入口方法在AbstractBeanDefinitionReader:

    //加载Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

    //解析int loadCount = loadBeanDefinitions(resources);

    最终逐个调用XmlBeanDefinitionReader的loadBeanDefinitions方法:

    Resource是代表一种资源的接口,其类图:

    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;
    
    }
    

    }

    其实也差不多。

    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********

    }

    实际这个documentLoader就是DefaultDocumentLoader. 此类是DocumentLoader接口的唯一实现。getEntityResolver方法返回ResourceEntityResolver,上面说过了。errorHandler是一个SimpleSaxErrorHandler对象。

    校验模型其实就是确定xml文件使用xsd方式还是dtd方式来校验,忘了的话左转度娘。Spring会通过读取xml文件的方式判断应该采用哪种。

    NamespaceAware默认false,因为默认配置了校验为true。(其实这个默认校验为true我还没找到)

    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比较有意思:

    如上图所示,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(二)

    本文来源于:宋文超super,专属平台有csdn、思否(SegmentFault)、 简书、 开源中国(oschina),转载请注明出处。

    相关文章

      网友评论

        本文标题:spring源码解析---spring-core(一)

        本文链接:https://www.haomeiwen.com/subject/nrzmlktx.html