spring源码学习(3) -- xml文件元素的硬编码处理

代码

本Markdown编辑器使用[StackEdit][6]修改而来,用它写博客,将会带来全新的体验哦:
在上一节中提到spring如何根据schemaLocation来查找对应的XSD、DTD文件。如果我们增加一个XSD、DTD文件可以根据上一节中的规则来处理。但是我们增加了XSD、DTD文件,我们是想把其中定义的内容添加到spring的 beanFactory。这个时候,应该怎么做呢。

spring 对于bean的加载

我们以xmlBeanFactory作为例子来分析一下spring如何将xml文件中的配置转换为对应的BeanDefinition接口的实现。

1.xmlBeanFactory初始化代码

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        // 使用XmlBeanDefinitionReader类来加载资源文件
        this.reader.loadBeanDefinitions(resource);
    }

2.对于资源文件的处理

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        // 将Resource转换为带编码的Resource
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    @Override
    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();
        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 {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 加载spring的bean
                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();
            }
        }
    }

3.将xml文件解析为document

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //将resource文件流转换为Document资源(上一节中有提到如何查找对于的XSD、DTD文件,就是在这里进行)
            Document doc = doLoadDocument(inputSource, resource);
            // 注册bean
            return registerBeanDefinitions(doc, resource);
        }
        //....
    }
    /**
     * 注册bean
     **/
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 此处将对于bean的注册委托给了BeanDefinitionDocumentReader来进行处理
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

4 处理spring的profile

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        // 判断是否是spring-bean的文件
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                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);
        // 解析文件并生成对应的BeanDefinnition
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
    /**
     * 解析文件并生成对应的BeanDefinnition
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 是否是spring自带的beans空间
        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 {
            //如果是自定义的元素,需要自行处理
            //在这里spring会根据不同的namespace去进行处理
            delegate.parseCustomElement(root);
        }
    }

5 根据namespace获取对应的解析器

    @Nullable
    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }
    /**
     * spring对于如何解析自定义标签都是在这个方法中进行处理
     * 
     */
    @Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // 根据元素信息获取namespace
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 根据namespace来获取到对应的NamespaceHandler,如果没有自定义namespacehandlerResolve,那么会使用默认的DefaultNamespaceHandlerResolver
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        // 解析对应的标签
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

6.DefaultNamespaceHandlerResolver

public NamespaceHandler resolve(String namespaceUri) {
        // 获取所有的handler信息
        Map<String, Object> handlerMappings = getHandlerMappings();
        // 根据namespace来获取指定的handler
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                // 调用namespaceHandler的初始化接口
                namespaceHandler.init();
                // 将新加载的namespaceHandler存储在内存中方便下次调用
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "] not found", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "]: problem with handler class file or dependent class", err);
            }
        }
    }
    /*
     * 获取配置文件中的硬编码
     */
    private Map<String, Object> getHandlerMappings() {
        Map<String, Object> handlerMappings = this.handlerMappings;
        if (handlerMappings == null) {
            synchronized (this) {
                handlerMappings = this.handlerMappings;
                if (handlerMappings == null) {
                    try {
                        // 加载所有的(META-INF/spring.handlers)文件为Properties
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }
                        Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
                        handlerMappings = mappingsToUse;
                        this.handlerMappings = handlerMappings;
                    }
                    catch (IOException ex) {
                        throw new IllegalStateException(
                                "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                    }
                }
            }
        }
        return handlerMappings;
    }

我们可以查看一下spring自带的一些spring.handlers文件

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

此时我们可以根据对应的namespace查找到对应的Handler处理类了,就可以对文件中的元素进行处理了
7.NamespaceHandlerSupport
spring并没有把对于xml的硬编码解析放在这里,只是会通过元素的第一级属性,去找到对应的BeanDefinitionParserr,BeanDefinitionParser才是真正对文件进行硬编码解析的地方。

    /**
     * 为一级元素注册对应的解析类
     *
     **/
    @Override
    public void init() {
        registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
        registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
        registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
        registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
        registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
        registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
    }

如何实现自定义的类型

  • 自定义对应的XSD、DTD文件放置在JAR包中
  • 添加META-INF/spring.schemas,其中根据自定的schemaLoction来映射到自定义的XSD、DTD文件位置
  • 继承NamespaceHandlerSupport,实现自己的namespaceHandler,需要重写其中的init()方法,并在其中为第一层元素指定对应的BeanDefinitionParser
  • 添加META-INF/spring.handlers,根据自定的namespace来映射到自己的namespaceHandler上
  • 实现BeanDefinitionParser,硬编码解析XML文件,并将解析生成的BeanDefinition实现注册到BeanFactory
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值