自定義標(biāo)簽解析

在很多情況下,我們需要為系統(tǒng)提供可配置化支持,簡單的做法可以直接基于Spring的標(biāo)準(zhǔn) bean 來配置,但配置較為復(fù)雜或者需要更多豐富控制的時(shí)候,會顯得非常笨拙。一般的做法會用原生態(tài)的方式去解析定義好的XML文件,然后轉(zhuǎn)化為配置對象。這種方式當(dāng)然可以解決所有問題,但實(shí)現(xiàn)起來比較繁瑣,特別是在配置非常復(fù)雜的時(shí)候,解析工作是一個不得不考慮的負(fù)擔(dān)。Spring提供了可擴(kuò)展Schema的支持,這是一個不錯的折中方案,擴(kuò)展Spring自定義標(biāo)簽配置大致需要以下幾個步驟(前提是要把Spring的Core包加入項(xiàng)目中)。

  • 創(chuàng)建一個需要擴(kuò)展的組件。
  • 定義一個XSD文件描述組件內(nèi)容。
  • 創(chuàng)建一個文件,實(shí)現(xiàn)BeanDefinitionParser接口,用來解析XSD文件中的定義和組件定義。
  • 創(chuàng)建一個Handler文件,擴(kuò)展自NamespaceHandlerSupport,目的是將組件注冊到Spring容器。
  • 編寫Spring.handlers和Spring.schemas文件。

現(xiàn)在我們就按照上面的步驟帶領(lǐng)讀者一步步地體驗(yàn)自定義標(biāo)簽的過程。

簡單例子

  • 定義一個對象User ,帶解析
public class User {
 private String userName;
 private String email;
 ...
}
  • 對象解析器UserBeanDefinitionParser
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    //Element對應(yīng)的類
    protected Class getBeanClass(Element element) {
        return User.class;
    }

    //從element中解析并提取對應(yīng)的元素
    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");
        //將提取的數(shù)據(jù)放入到BeanDefinitionBuilder中,待到完成所有bean的解析后統(tǒng)一注冊到beanFactory中
        if (StringUtils.hasText(userName)) {
            bean.addPropertyValue("userName", userName);
        }
        if (StringUtils.hasText(email)) {
            bean.addPropertyValue("email", email);
        }
    }
}
  • 注冊解析器MyNamespaceHandler
public class MyNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    }
}
  • xsd定義,user.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.lexueba.com/schema/user"
        xmlns:tns="http://www.lexueba.com/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
         <attribute name="id" type="string"/>
         <attribute name="userName" type="string"/>
         <attribute name="email" type="string"/>
        </complexType>
    </element>
</schema>
  • spring.schemas定義
http\://www.lexueba.com/schema/user.xsd=META-INF/user.xsd
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myname="http://www.lexueba.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.lexueba.com/schema/user
       http://www.lexueba.com/schema/user.xsd">
    <myname:user id="testbean" userName="aaa" email="bbb"/>
</beans>
  • 調(diào)用
    @Test
    public void customerUserLoad() throws IOException {
        ApplicationContext bf = new ClassPathXmlApplicationContext ("user.xml");
        User user=(User) bf.getBean("testbean");
        System.out.println(user.getUserName()+","+user.getEmail());
    }
  • 運(yùn)行結(jié)果
aaa,bbb

源碼分析

我們上面定義了很多東西,目標(biāo)就是能實(shí)現(xiàn)以下定義,來初始化User對象

<myname:user id="testbean" userName="aaa" email="bbb"/>

我們首先分析,根據(jù)命名空間,獲取handler

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
    @Override
    public NamespaceHandler resolve(String namespaceUri) {
        //通過 "META-INF/spring.handlers"定義
        Map<String, Object> handlerMappings = getHandlerMappings();
        //獲取相應(yīng)handler ClassName
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            ....
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            ....
        }
        else {
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                //實(shí)例化,并調(diào)用init方法,
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                //init方法,主要注冊我們的parser,下一步使用
                namespaceHandler.init();
                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);
            }
        }
    }
}

根據(jù)handler獲取parse解析器

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return findParserForElement(element, parserContext).parse(element, parserContext);
    }

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        String localName = parserContext.getDelegate().getLocalName(element);
        //這里的parsers在前面init方法注冊了。獲得解析器。
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }
}

根據(jù)解析器調(diào)用parse,并后置處理

public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
    @Override
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
            ...
        return definition;
    }


}

public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {
    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        ....
        //調(diào)用定義的doParse方法。
        doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        doParse(element, builder);
    }
}

這里parseInternal,會調(diào)用AbstractSingleBeanDefinitionParser 類的parseInternal是因?yàn)槲覀兌x的解析器繼承的這個類AbstractSingleBeanDefinitionParser

public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容