1. 程式人生 > >Thymeleaf3自定義方言Dialect與處理器

Thymeleaf3自定義方言Dialect與處理器

Thymeleaf為了方便擴充套件,定義了方言Dialect這個概念與元件。綜合我個人的理解,簡而言之方言就是Thymeleaf用於渲染檔案的處理器Processor以及表示式Expression的集合體。 當模板為HTML時,處理器Processor處理的是具體標籤Tag的內容。其他格式的模板暫還沒有使用到。 Thymeleaf本身提供了StandardDialect,以及結合了Spring之後提供的SpringStandardDialect。Thymeleaf預設的語法 th:if等,就是定義在了StandardDialect中,th為方言的字首,if為方言的處理器名稱。 StandardDialect

的原始碼中定義瞭如下的內容:

public class StandardDialect
            extends AbstractProcessorDialect
            implements IExecutionAttributeDialect, IExpressionObjectDialect {

    public static final String NAME = "Standard";
    public static final String PREFIX = "th";
    public static final int PROCESSOR_PRECEDENCE =
1000; ...

其中的 PREFIX = "th" 定義了在模板中使用時,需要以 th:XX 的形式呼叫。 詳細的介面介紹,可以檢視:官方文件 ,本篇文章中暫不進行介紹。 先按照原始碼的例項,進行我們自己的方言與表示式的編寫。

建立 processor

自定義的處理器,分為幾種,此次先使用 IElementProcessor 介面,此介面為元素Element處理的基礎介面。 但官方建議一般不要直接實現此介面實現我們自己的處理器,而是繼承抽象類 AbstractAttributeTagProcessor

public class CodeValueProcessor extends
AbstractAttributeTagProcessor { private static final String ATTR_NAME = "codeValue"; private static final int PRECEDENCE = 12000; public CodeValueProcessor(final String dialectPrefix) { super( TemplateMode.HTML, // This processor will apply only to HTML mode dialectPrefix, // Prefix to be applied to name for matching null, // No tag name: match any tag name false, // No prefix to be applied to tag name ATTR_NAME, // Name of the attribute that will be matched true, // Apply dialect prefix to attribute name PRECEDENCE, // Precedence (inside dialect's precedence) true); // Remove the matched attribute afterwards } @Override protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, String attributeValue, IElementTagStructureHandler structureHandler) { ...

上方程式碼中的 ATTR_NAME 定義了處理器在被呼叫時候的名稱。繼承了 AbstractAttributeTagProcessor 後,強制需要實現的方法是 doProcess 。此方法內編寫具體的功能程式碼,如下:

@Override
    protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName,
            String attributeValue, IElementTagStructureHandler structureHandler) {
         final IEngineConfiguration configuration = context.getConfiguration();

            /*
             * Obtain the Thymeleaf Standard Expression parser
             * 獲取Thymeleaf的表示式轉換器
             */
            final IStandardExpressionParser parser =
                    StandardExpressions.getExpressionParser(configuration);

            /*
             * Parse the attribute value as a Thymeleaf Standard Expression
             */
            final IStandardExpression expression = parser.parseExpression(context, attributeValue);
            
            /*
             * Execute the expression just parsed
             * 使用得到的表示式,處理上下文內容,得到具體傳入的引數值
             * Demo中傳入的是一個數字
             */
            final Integer position = (Integer) expression.execute(context);
            
            /*
             * Create the DOM structure that will be substituting our custom tag.
             */
            final IModelFactory modelFactory = context.getModelFactory();
            final IModel model = modelFactory.createModel();
            //根據傳入的引數,修改 Tag 的具體內容。
            if(position.equals(0)) {
                model.add(modelFactory.createOpenElementTag("td", "style", "background:red"));
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5("無效")));
            }else {
                model.add(modelFactory.createOpenElementTag("td"));
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5("啟用")));
            }
            model.add(modelFactory.createCloseElementTag("td"));

            /*
             * Instruct the engine to replace this entire element with the specified model.
             * 通過 structureHandler 修改tag標籤的內容
             */
            structureHandler.replaceWith(model, false);
        
    }

建立 BaseDialect

如之前所說的,Dialect是集合,因此需要建立自定義的方言 BaseDialect 類,然後引入具體的處理器。 同樣不直接實現介面,而是繼承了 AbstractProcessorDialect 抽象類,同時需要指定名稱,以及字首 prefix。

public class BaseDialect extends AbstractProcessorDialect implements IExpressionObjectDialect {

    private static final String DIALECT_NAME = "BaseDialect";

    private static final String PREFIX = "base";

    public BaseDialect() {
        super(DIALECT_NAME, PREFIX, StandardDialect.PROCESSOR_PRECEDENCE);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {
        /*
         * 註冊processors的地方
         */
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(new CodeValueProcessor(dialectPrefix));
        
        // This will remove the xmlns:score attributes we might add for IDE validation
        processors.add(new StandardXmlNsTagProcessor(TemplateMode.HTML, dialectPrefix));
        return processors;
    }

這麼定義之後,在模板使用自定義的 BaseDialect 方言的形式就是 base:codeValue 了。

註冊自定義方言

由於筆者使用的是SpringBoot+SpringMVC,所以註冊比較簡單,在configuration類中定義如下的方法:

@Bean
@ConditionalOnMissingBean
public BaseDialect baseDialect() {
    //增加Thymeleaf的方言,支援一些自定義的模板表示式
    return new BaseDialect();
}

模板中的具體使用

<td base:codeValue="${item.status}"></td>