菜鸟入坑》spring源码初步认识(对XML的解析)

1.XML验证模式的认识

首先XML的验证模式有两种:DTD和XSD。 

DTD文档类型定义,是XML约束模式语言。它是为了保证XML文档格式正确有效的方法。通过XML文档和DTD文档的比较来判断XML是否符合规范。(现在我很少见,不知道是不是淘汰了)

举个例子:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN"
        "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd">
<beans>
...   
</beans>

XSD(XML Schemas Definition),它描述了XML文档的结构和规范,可以指定一个XML文档允许的结构和内容,通过这样就可以校验某一个XML文档是否有效。(这种现在最常见!)

举个例子:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

上面的http://www.springframework.org/schema/beans/spring-beans.xsd,这个命名空间,就指定了xml中bean的验证方式。 具体的校验我就不介绍了,每一个命名空间都可以查看对应的校验内容 。

2.通过ClassPathXmlApplicationContext来认识spring

下面以我们刚接触spring时,通过ClassPathXmlApplicationContext获取bean的方式来认识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" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <bean id="user" class="support.mode.User">
        <property name="useId" value="00232"></property>
        <property name="userName" value="topsnowwolf"/>
    </bean>

</beans>

public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-mvc.xml");
        User user = applicationContext.getBean(User.class);
        System.out.println(user.getUseId()+":"+user.getUserName());
 }
认识前先了解类,接口的关系:


现在我们初步认识一下下面这几个对象: 

  • BeanFactory:定义获取bean以及bean的各种属性。
  • ListableBeanFactory:根据各种条件获取bean的配置清单。 
  • ResourceLoader:资源加载器 
  • AbstractApplicationContext:这个抽象类的refresh方法为是解析xml,获取,加载bean的入口。

在实例化ClassPathXmlApplicationContext对象时,会调用ClassPathXmlApplicationContext的构造方法创建对象,在创建

ClassPathXmlApplicationContext时,就会进行一系列的操作。

经过分析入口就是AbstractApplicationContext类的refresh方法。

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//获取BeanFactory配置清单
            this.prepareBeanFactory(beanFactory);
            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }
        }
    }

ConfigurableListableBeanFactory对象的创建。

ConfigurableListableBeanFactory:是BeanFactory配置清单。 

ConfigurableListableBeanFactory和DefaultListableBeanFactory的关系图。


获取BeanFactory配置清单

通过obtainFreshBeanFactory()方法:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        this.refreshBeanFactory();//获取DefaultListableBeanFactory对象。
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

refreshBeanFactory方法的认识: 由于AbstractApplicationContext类的refreshBeanFactory方法是抽象方法,实现类是AbstractRefreshableApplicationContext。

关系图:


protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());//给DefaultListableBeanFactory设置序列化ID
            this.customizeBeanFactory(beanFactory);//当我们配置了两个name属性相同的Bean时,spring默认会后面配置的Bean会覆盖掉前面配置的Bean对象
            this.loadBeanDefinitions(beanFactory);//
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

AbstractRefreshableApplicationContext的方法loadBeanDefinitions实现由AbstractXmlApplicationContext。
关系图:


protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

最后loadBeanDefinitions这方法绕来绕去,会调用到XmlBeanDefinitionReader中的loadBeanDefinitions方法。 到现在还没真正的解析XML,之前的工作全部是准备。是不是很绕啊。 

3.Spring解析XML成document的过程

XmlBeanDefinitionReader类中的loadBeanDefinitions就是重点了!!!

大概思路:

xml等配置会被注册到容器中,xml文件最终都会通过ResourceLoader加重成Resource对象,通Reader进行解析读取,最后注册到容器中,spring以sax的方式解析xml。

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

第一步:

XmlBeanDefinitionReader类的loadBeanDefinitions(Resource resource)方法将Resource对象封装成EncodedResource对象。(EncodedResource的作用是对资源文件进行编码格式处理,处理方法看EncodedResource类的getReader())

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }
public class EncodedResource implements InputStreamSource {
    private final Resource resource;
    private final String encoding;
    private final Charset charset;
    public EncodedResource(Resource resource) {
        this(resource, (String)null, (Charset)null);
    }
    public EncodedResource(Resource resource, String encoding) {
        this(resource, encoding, (Charset)null);
    }
    public EncodedResource(Resource resource, Charset charset) {
        this(resource, (String)null, charset);
    }
    private EncodedResource(Resource resource, String encoding, Charset charset) {
        Assert.notNull(resource, "Resource must not be null");
        this.resource = resource;
        this.encoding = encoding;
        this.charset = charset;
    }
....

第二步:

从EncodedResource中获取InputStream ,之后将InputStream 转化为InputSource。

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();//从EncodedResource中获取InputStream 。
                try {
                    InputSource inputSource = new InputSource(inputStream);//将InputStream 转化为InputSource?为何要转化为InputSource呢?下面解析xml时再解释。
                    if (encodedResource.getEncoding() != null) {//设置编码格式
                        inputSource.setEncoding(encodedResource.getEncoding());//
                    }
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());//进入解析XML的核心代码
                } 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;
        }
    }

第三步:

进入解析XML(小知识  xml的解析方式:jdk的提供的dom解析,dom4j,sax常用的三种。)

由于spring对xml的解释是采用sax方式进行解析的,所以现在大概知道为什么要将InputStream 转化为InputSource了吧。因为sax解析xml时,parse方法入参就必须是sax提供的InputSource流对象。

this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());//进入解析XML的核心代码
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            Document doc = this.doLoadDocument(inputSource, resource);//sax方式解释xml
            return this.registerBeanDefinitions(doc, resource);//注册bean
        } catch (BeanDefinitionStoreException var4) {
            throw var4;
        } catch (SAXParseException var5) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
        } catch (SAXException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
        } catch (ParserConfigurationException var7) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
        } catch (IOException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
        } catch (Throwable var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
        }
    }

初略说一下:doLoadDocument

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, 
         this.getEntityResolver(), //向SAX 驱动器注册一个实例EntityResolver,至于为何要一定要这个EntityResolver这个实例给SAX,就不多说了。
         this.errorHandler, //SimpleSaxErrorHandler对象
         this.getValidationModeForResource(resource), //XML验证方式的读取
         this.isNamespaceAware()  //
        );
    }
protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = this.getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {//如果手动指定了验证模式则使用指定的验证模式
            return validationModeToUse;
        } else {//没有自动检测
            int detectedMode = this.detectValidationMode(resource);
            return detectedMode != VALIDATION_AUTO ? detectedMode : VALIDATION_XSD;
        }
    }

调用DefaultDocumentLoader类中的loadDocument解析xml

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);//创建一个工厂(工厂模式)
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);//获取建造者(建造者模式)
        return builder.parse(inputSource);//解释XML成Document
    }

sax如何解析xml成Document这里就不废话了。

第四步:

将解析的document对应的bean注册到spring容器中。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();//实例化BeanDefinitionDocumentReader
        int countBefore = this.getRegistry().getBeanDefinitionCount();//总结已经存在的BeanDefinition个数
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));//加载注册bean  核心
        return this.getRegistry().getBeanDefinitionCount() - countBefore;//返回本次加载的BeanDefinition个数
    }
这里就是最最最核心的代码了。
BeanDefinitionDocumentReader是一个接口,而实例化由createBeanDefinitionDocumentReader()完成!
 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return (BeanDefinitionDocumentReader)BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }
通过这个可以知道,BeanDefinitionDocumentReader的实现类是DefaultBeanDefinitionDocumentReader。 下面我们重点看一下DefaultBeanDefinitionDocumentReader类中的registerBeanDefinitions()方法。

 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();//获取document的元素。
        this.doRegisterBeanDefinitions(root);
    }

此时我们才真真正正的看到了底层的实现方法。绕来绕去,终于看到了曙光!!!下面我们接着分析doRegisterBeanDefinitions(root)方法。

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);//实例化BeanDefinitionParserDelegate,是一个解析器代理类。
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");//处理profile属性,profile来实现动态生成相应的bean,如何实现网上很多例子,这里就不介绍了。https://www.cnblogs.com/yw0219/p/5990056.html,https://www.jianshu.com/p/948c303b2253这两篇文章介绍得很具体。
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
        this.preProcessXml(root);
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }
此时会发现,这里才是真真正正对XML的每一个节点进行解析! 大略看一下BeanDefinitionParserDelegate的,你发现它实际就是封装了bean的各种属性。
public class BeanDefinitionParserDelegate {
    public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
    public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
    public static final String TRUE_VALUE = "true";
    public static final String FALSE_VALUE = "false";
    public static final String DEFAULT_VALUE = "default";
    public static final String DESCRIPTION_ELEMENT = "description";
    public static final String AUTOWIRE_NO_VALUE = "no";
    public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
    public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
    public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
    public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
    public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";
    public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";
    public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String BEAN_ELEMENT = "bean";
    public static final String META_ELEMENT = "meta";
    public static final String ID_ATTRIBUTE = "id";
    public static final String PARENT_ATTRIBUTE = "parent";
    public static final String CLASS_ATTRIBUTE = "class";
    public static final String ABSTRACT_ATTRIBUTE = "abstract";
    public static final String SCOPE_ATTRIBUTE = "scope";
    private static final String SINGLETON_ATTRIBUTE = "singleton";
    public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
    public static final String AUTOWIRE_ATTRIBUTE = "autowire";
    public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
    public static final String PRIMARY_ATTRIBUTE = "primary";
    public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";
    public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
    public static final String INIT_METHOD_ATTRIBUTE = "init-method";
    public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
    public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
    public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
    public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
    public static final String INDEX_ATTRIBUTE = "index";
    public static final String TYPE_ATTRIBUTE = "type";
    public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
    public static final String KEY_TYPE_ATTRIBUTE = "key-type";
    public static final String PROPERTY_ELEMENT = "property";
    public static final String REF_ATTRIBUTE = "ref";
    public static final String VALUE_ATTRIBUTE = "value";
    public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
    public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
    public static final String REPLACER_ATTRIBUTE = "replacer";
    public static final String ARG_TYPE_ELEMENT = "arg-type";
    public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
    public static final String REF_ELEMENT = "ref";
    public static final String IDREF_ELEMENT = "idref";
    public static final String BEAN_REF_ATTRIBUTE = "bean";
    public static final String LOCAL_REF_ATTRIBUTE = "local";
    public static final String PARENT_REF_ATTRIBUTE = "parent";
    public static final String VALUE_ELEMENT = "value";
    public static final String NULL_ELEMENT = "null";
    public static final String ARRAY_ELEMENT = "array";
    public static final String LIST_ELEMENT = "list";
    public static final String SET_ELEMENT = "set";
    public static final String MAP_ELEMENT = "map";
    public static final String ENTRY_ELEMENT = "entry";
    public static final String KEY_ELEMENT = "key";
    public static final String KEY_ATTRIBUTE = "key";
    public static final String KEY_REF_ATTRIBUTE = "key-ref";
    public static final String VALUE_REF_ATTRIBUTE = "value-ref";
    public static final String PROPS_ELEMENT = "props";
    public static final String PROP_ELEMENT = "prop";
    public static final String MERGE_ATTRIBUTE = "merge";
    public static final String QUALIFIER_ELEMENT = "qualifier";
    public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
    public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
    public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
    public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
    public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
    public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
    public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
    public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final XmlReaderContext readerContext;
    private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
    private final ParseState parseState = new ParseState();
    private final Set<String> usedNames = new HashSet();
    ......
parseBeanDefinitions()这个方法必须重点认识!!!
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        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)) {
                        this.parseDefaultElement(ele, delegate);//解析默认的bean
                    } else {
                        delegate.parseCustomElement(ele);//解析自定义的bean
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }
    }

不同bean的认识:

默认的bean:

<bean id="user" class="support.mode.User">
        <property name="useId" value="00232"></property>
        <property name="userName" value="topsnowwolf"/>
    </bean>
自定义的bean:
<tx:annotation-driven />

这两者区别很大,如果是spring默认配置的bean,spring肯定知道如何处理,自定义的就要实现一下接口和配置。 问题spring是如何知道是默认的还是自定义的呢?

public boolean isDefaultNamespace(String namespaceUri) {
        return !StringUtils.hasLength(namespaceUri) || "http://www.springframework.org/schema/beans".equals(namespaceUri);
    }
    public boolean isDefaultNamespace(Node node) {
        return this.isDefaultNamespace(this.getNamespaceURI(node));
    }

判断是默认的还是自定义的,通过标签对应的命名空间去判断。

当命名空间是http://www.springframework.org/schema/beans时,就是默认的。下次我将总结一下如何自定义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"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx.xsd
                        http://www.springframework.org/schema/aop
                        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 自动扫描 -->
    <context:component-scan base-package="support.*"/>
    <tx:annotation-driven />
    <!-- 第一种方式:加载一个properties文件 -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"/>
    </bean>

上面这一部分配置:

我们可以看到 context标签对应的命名空间就是http://www.springframework.org/schema/context。

tx的是http://www.springframework.org/schema/tx。

差不多了!!!

最后总结一下整个过程:


补充说明一下:spring对默认标签的解析和自定义标签的解析时间有限在这里就不多说了,下篇见!

个人才疏学浅,可能很多地方写得不对,还希望大牛给与指导,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值