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>