1. 程式人生 > >spring原始碼-自定義標籤-4

spring原始碼-自定義標籤-4

  一、自定義標籤,自定義標籤在使用上面相對來說非常常見了,這個也算是spring對於容器的拓展。通過自定義標籤的方式可以創造出很多新的配置方式,並且交給容器直接管理,不需要人工太多的關注。這也是spring對於配置拓展的一個很重要的方式。

  二、自定義標籤的幾個步驟:1、建立可掃描的標籤和對應的解析類  2、讀取頁面元素解析 3、加入容器管理

  三、涉及到的常用類:BeanDefinitionParser、NamespaceHandlerSupport;檔案:spring.handlers、spring.schemas、*.xsd

  四、實現過程:

  1)需要實現的類:

  

  pojo:

public class User{

    private String id;
    private String name;
    private String age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void
setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }

  UserBeanDefinitionParser:

public class UserBeanDefinitionParser implements BeanDefinitionParser {

    //解析xml
public BeanDefinition parse(Element element, ParserContext parserContext) { //通過讀取xml的元素來實現具體配置 String id = element.getAttribute("id"); String name = element.getAttribute("name"); String age = element.getAttribute("age"); //註冊到spring容器 BeanDefinitionRegistry registry = parserContext.getRegistry(); BeanDefinition beanDefinition = null; try { //這裡的RootBeanDefinition為我們常用的註冊形式 beanDefinition = new RootBeanDefinition(User.class); beanDefinition.getPropertyValues().add("id", id); beanDefinition.getPropertyValues().add("name", name); beanDefinition.getPropertyValues().add("age", age); //註冊 registry.registerBeanDefinition(id, beanDefinition); } catch (Exception e) { e.printStackTrace(); } return beanDefinition; } }

  UserNameSpaceHandler:

//用於提供namespace的掃描
public class UserNameSpaceHandler extends NamespaceHandlerSupport {

    //初始化
    public void init() {
        //註冊解析手段
        registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
    }
}

  2)因為來了,這個沒有啥關聯啊。那麼久需要用到具體的配置來做關聯(spring.handlers、spring.schemas、*.xsd)

  

  備註:這個自己預設配置在resources裡面就可以。目錄必須為META-INF,預設兩個檔案:spring.handlers、spring.schemas。標籤:*.xsd

  spring.handlers:預設處理的NamespaceHandlerSupport

http\://www.pinnet.com/schema/user=com.pinnet.customLabel.UserNameSpaceHandler

  spring.schemas:用於使用的標籤格式以及驗證

http\://www.pinnet.com/schema/user.xsd=META-INF/user.xsd

  user.xsd:這裡寫了一個簡單的例子(標籤的編寫這裡不做介紹,可以自己查詢schema的官網

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
        xmlns="http://www.pinnet.com/schema/user"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="http://www.pinnet.com/schema/user"
        elementFormDefault="qualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    <xsd:element name="user">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="name" type="xsd:string"/>
                    <xsd:attribute name="age" type="xsd:string"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

  3)好了上面的基本準備工作ok了。具體就是容器做的處理了,我們來關注原始碼的實現部分。

  spring-bean.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:pinnet="http://www.pinnet.com/schema/user" 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.pinnet.com/schema/user http://www.pinnet.com/schema/user.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <pinnet:user id="user1" name="name1" age="age1"/>
    <pinnet:user id="user2" name="name2" age="age2"/>
    <pinnet:user id="user3" name="name3" age="age3"/>
</beans>

  注意:有下劃線的部分,這裡就是引用過後,然後需要進行的配置。xmlns:pinnet中的pinnet可以自己隨便取

  測試:

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        User user1 = (User) context.getBean("user1");
        User user2 = (User) context.getBean("user2");
        User user3 = (User) context.getBean("user3");
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user3);
    }
}

  4)好了重點來了,原始碼部分。

  這裡從自定義標籤開始講起:其他部分可以參考:spring原始碼-bean之初始化-1到7)部分

  parseCustomElement

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)) {
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        //這裡就是自定義的配置方式
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //如果根節點不是自定義的那就自己自定義處理
            delegate.parseCustomElement(root);
        }
    }
public BeanDefinition parseCustomElement(Element ele) {
        //解析配置
        return this.parseCustomElement(ele, (BeanDefinition)null);
    }

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //獲取Namespace的uri
        String namespaceUri = this.getNamespaceURI(ele);
        //然後獲取NamespaceHandler的實現類NamespaceHandlerSupport的對應實現
        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 {
            //處理、解析
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }

  resolve:

public NamespaceHandler resolve(String namespaceUri) {
        //獲取所有NamespaceHandler的NamespaceHandlerSupport實現類(過程不詳解了)
        Map<String, Object> handlerMappings = this.getHandlerMappings();
        //獲取具體的NamespaceHandlerSupport實現類
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        } else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler)handlerOrClassName;
        } else {
            //我們預設第一次通過spring.handlers,一般都是String型別的
            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 = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                    //呼叫初始化這裡的初始化檢視前面的呼叫
                    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);
            }
        }
    }

  parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
        //發現解析的BeanDefinitionParser,並呼叫實現類的parse方法
        return this.findParserForElement(element, parserContext).parse(element, parserContext);
    }

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        //獲取本地的localName,在NamespaceHandlerSupport中進行了init,所以會直接獲取到UserNameSpaceHandler
        String localName = parserContext.getDelegate().getLocalName(element);
        BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }

        return parser;
    }

  五:上面基本的實現過程就是這樣子了,包含了原始碼的實現邏輯。這裡需要關注的點在於匹配的過程。其他都是自己手動完成的。

  六、用處:相對於我們人的解析過程,自定義標籤的解析的過程並不是很複雜,更多需要自己手動去完成解析。自定義標籤的好處在於,可以很大程度的減少程式碼冗餘的情況。通過同一套流程,開發者只需要關注配置就可以了,而不需要關注具體的解析過程和實現邏輯。另外可以更加方便的與spring容器進行深度的整合!