自定义标签的解析

1.自定义标签的解析

Spring中的标签分为默认标签和自定义标签两种,之前分析了默认标签的解析。当配置文件到Document的转换并提取了相对应的root之后,开始了所有元素的解析,这里解析时便开始了默认标签和自定义标签的两种区分,源码如下:

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);
                    } else {
                        //自定义标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //自定义标签
            delegate.parseCustomElement(root);
        }
    }

由上面的源码可知,当Spring拿到一个元素时,首先进行了命名空间的解析,如果是默认的命名空间,则使用parseDefaultElement方法进行解析,否则就使用parseCustomElement方法进行解析。

自定义标签解析
parseCustomElement方法:

 public BeanDefinition parseCustomElement(Element ele) {
        return this.parseCustomElement(ele, (BeanDefinition)null);
    }

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //获取命名空间
        String namespaceUri = this.getNamespaceURI(ele);
       //根据命名空间找到对应的NameSpaceHandler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        } else {
            //调用自定义的handler进行解析
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
}

1.1 获取标签的命名空间

标签的解析是从命名空间的提起开始的,在Spring中无论是默认标签和自定义标签都是以标签所提供的的命名空间为基础的,在org.w3c.dom.Node中已经提供了方法:

public String getNameSpaceURI(Node node){
      return node.getNameSpaceURI();
}

1.2 提取自定义标签处理器

有了命名空间之后,就可以进行NamespaceHandler的提取,NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 这里调用了DefaultNamespaceHandlerResolver中的resolve方法:

public NamespaceHandler resolve(String namespaceUri) {
        //获取所有的已经配置好的handler映射
        Map handlerMappings = this.getHandlerMappings();
        //根据命名空间找到对应的信息
        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");
                } else {
                    //初始化类
                    NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
                    //自定义的NamespaceHandler初始化方法
                    namespaceHandler.init();
                    //记录在缓存中。
                    handlerMappings.put(namespaceUri, namespaceHandler);
                    return namespaceHandler;
                }
            } catch (ClassNotFoundException var7) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
            } catch (LinkageError var8) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
            }
        }
}

getHandlerMappings()方法:

private Map getHandlerMappings() {
        if (this.handlerMappings == null) {
            //存在多线程并发
            synchronized(this) {
                if (this.handlerMappings == null) {
                    try {
                    //location路径在构造时默认META-INF
                        Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }
          //将properties文件转化为Map格式的handMapping
                        Map handlerMappings = new ConcurrentHashMap(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    } catch (IOException var5) {
                        throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);
                    }
                }
            }
        }

        return this.handlerMappings;
    }

1.3 标签解析:

得到了解析器以及要分析的元素,Spring将解析工作委托给自定义解析器去解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
NamespaceHandlerSupport.java中的parse方法:

 public BeanDefinition parse(Element element, ParserContext parserContext) {
        //寻找解析器并操作
        return this.findParserForElement(element, parserContext).parse(element, parserContext);
}
    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        //获取元素的名称,也就是

操作的方法
this.findParserForElement(element, parserContext).parse(element, parserContext);

public final BeanDefinition parse(Element element, ParserContext parserContext) {
        //创建AbstractBeanDefinition对象
        AbstractBeanDefinition definition = this.parseInternal(element, parserContext);
        if (definition != null && !parserContext.isNested()) {
            try {
               //id,alias 
 String id = this.resolveId(element, definition, parserContext);
                if (!StringUtils.hasText(id)) {
                    parserContext.getReaderContext().error("Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element);
                }

                String[] aliases = null;
                if (this.shouldParseNameAsAliases()) {
                    String name = element.getAttribute("name");
                    if (StringUtils.hasLength(name)) {
                        aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                    }
                }
             //将AbstractBeanDefinition转化为BeanDefinitionHolder
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                this.registerBeanDefinition(holder, parserContext.getRegistry());
                if (this.shouldFireEvents()) {
                 //通知监听器进行处理
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                    this.postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            } catch (BeanDefinitionStoreException var8) {
                parserContext.getReaderContext().error(var8.getMessage(), element);
                return null;
            }
        }

        return definition;
    }

在上面的parse方法中,第一句parseInternal()主要是对自定义方法的调用:

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        String parentName = this.getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }

        //获取自定义标签的class,此时会调用自定义解析器eg:UserBeanDefinitionParse中的getBeanClass方法 
  Class beanClass = this.getBeanClass(element);
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        } else {
          //若子类没有重写getBeanClass方法,尝试检查子类是否重写了getBeanClassName方法
            String beanClassName = this.getBeanClassName(element);
            if (beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }

        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        if (parserContext.isNested()) {
           //如果父类存在则使用父类的scope属性
            builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }

        if (parserContext.isDefaultLazyInit()) {
           //设置延迟加载
            builder.setLazyInit(true);
        }
       //调用子类重写的doParse方法进行解析
        this.doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

以上就是Spring如何加载解析自定义标签的过程,到现在为止,已经学习了Spring将bean从配置文件到加载内存的全过程。

2.自定义标签使用

很多情况下,开发中需要为系统提供可配置化支持,最简单的是直接基于Spring的标准bean来配置,但配置较为复杂的或者需要更多丰富控制的时候,会显得很笨拙,一般的做法是会用原生态的方式去解析定义好的XML文件,然后转化为配置对象。实现起来比较繁琐。Spring提供了可扩展的Schema的支持,扩展Spring的自定义标签需要配置一下步骤(需要Spring的core包)
1.创建一个需要扩展的组件
2.定义一个XSD文件描述组件内容
3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
4.创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件扩展至Spring容器
5.编写Spring.handlers和Spring.schemas文件

2.1创建一个普通的pojo对象:

package pojo;

public class User {
    private String userName;
    private String email;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

2.2 定义一个XSD文件描述组件内容



	
			 
				 
				 
				 
			 
	


2.3创建UserBeanDefinitionParser

创建一个类,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。

package com.SpringBean.test;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import pojo.User;

public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    /**
     * Element对应的类
     */
    @Override
    protected Class getBeanClass(Element element) {
        return User.class;
    }

    /**
     * 从element中提取对应的元素
     * @param element
     * @param builder
     */
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String userName=element.getAttribute("userName");
        String email=element.getAttribute("email");
        if(StringUtils.hasText(userName)){
            builder.addPropertyValue("userName",userName);
        }
        if(StringUtils.hasText(email)){
            builder.addPropertyValue("email",email);
        }
    }
}

2.4 创建Handler文件

创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是为了将组件注册到Spring容器中

package com.SpringBean.test;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNameSpaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
    }
}

2.5编写Spring.handlers和Spring.schemas文件

Spring.handlers:

http\://www.test.com/sehema/user=com.SpringBean.test.MyNameSpaceHandler

Spring.schemas:

http\://www.test.com/sehema/user.xsd=META-INF/userschema.xsd

在上面的源码解析中可知,遍历这两个文件默认的location是META-INF路径
所以要将文件放在classpath:META-INF包里,或者修改源码即可。
beans.xml:



     


test:

package com.SpringBean.test;

import bean.GetBeanTest;
import bean.TestChangeMethod;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import pojo.User;

@SuppressWarnings("deprecation")
public class SpringTest {

    public static void main(String[] args) throws Exception {
     //   Resource resource = new ClassPathResource("beans.xml");
        BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
/*        GetBeanTest test=(GetBeanTest) bf.getBean("getBeanTest");
        TestChangeMethod me=(TestChangeMethod) bf.getBean("testChangeMethod");
        me.changeMe();*/
        User user=(User) bf.getBean("testBean");
        System.out.println(user.getUserName()+" "+user.getEmail());
      //  test.showMain();
    }



}

运行结果:
zhangsan [email protected]

你可能感兴趣的:(Java,Spring)