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
的形式调用。
详细的接口介绍,可以查看:官方文档 ,本篇文章中暂不进行介绍。
先按照源代码的实例,进行我们自己的方言与表达式的编写。
自定义的处理器,分为几种,此次先使用 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);
}
如之前所说的,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>