thymeleaf 自定义标签&方言和处理器简介

1.概念

1.1 Dialects

thymeleaf是一个容易扩展的库,大部分面向用户的功能不是直接构建在他的核心中,而是通过打包和组件化到一个称谓Dialect(方言)的功能集合中,同时可以自定义一组attribute或者tag在thymeleaf中用来处理自定义的模板。

Dialects是实现了org.thymeleaf.dialect.IDialect接口的对象,具体如下:

public interface IDialect {
    public String getName();
}

基础的接口有:

  • IProcessorDialect 处理器方言
public interface IProcessorDialect extends IDialect {
    public String getPrefix();//应用于匹配元素和属性的前缀
    public int getDialectProcessorPrecedence();//定义方言优先级
    public Set<IProcessor> getProcessors(final String dialectPrefix);//定义一组处理器集合
}
  • IPreProcessorDialect 预处理方言
public interface IPreProcessorDialect extends IDialect {
    public int getDialectPreProcessorPrecedence();
    public Set<IPreProcessor> getPreProcessors();
}

ps:处理器方言是在单个时间或者模板片段上执行。而预处理方言和后处理方言是作为引擎处理过程中的附加步骤,应用在整个模板的执行过程中。

  • IPostProcessorDialect 后处理方言
  • IExpressionObjectDialect 表达式对象方言
public interface IExpressionObjectDialect extends IDialect {
    public IExpressionObjectFactory getExpressionObjectFactory();
}

ps:Dialect可以提供新的表达式对象或者表达式应用程序对象,例如#strings,#numbers等

  • IExecutionAttributeDialect 可执行属性方言,即在模板处理期间执行的每个处理器可用的对象
public interface IExecutionAttributeDialect extends IDialect {
    public Map<String,Object> getExecutionAttributes();
}

1.2 Processors处理器

处理器的对象全部实现org.thymeleaf.processor.IProcessor接口,具体如下:

public interface IProcessor {
    public TemplateMode getTemplateMode();
    public int getPrecedence();
}

基础的接口有:

  • IElementProcessor 元素处理器,

    在open element 或者独立元素上执行,IElementProcessor处理器并不是直接实现这个接口,它还包含了两个子接口
    • IElementTagProcessor
    • IElementModelProcessor

2. 自定义标签方言

步骤:

  1. 定义方言
  2. 定义方言处理器
  3. 添加到thymeleaf引擎中
//spring boot配置启用
    @Bean
    @ConditionalOnMissingBean
    public WorkFocusDialect wlfDialect() {
        return new WorkFocusDialect();
    }

2.1 表格属性实例

eg:table表格中某一列显示内容为是否启用,具体的值为0和1.如果是0,该单元格是红色,否则为绿色。

  • 自定义方言
public class WorkFocusDialect extends AbstractProcessorDialect {
    private final IExpressionObjectFactory EXPRESSION_OBJECTS_FACTORY = new WorkFocusExpressionFactory();
    private static final String DIALECT_NAME = "workfocus";
    private static final String PREFIX = "wlf";
    public static final int PROCESSOR_PRECEDENCE = 1000;

    public WorkFocusDialect() {
        // We will set this dialect the same "dialect processor" precedence as
        // the Standard Dialect, so that processor executions can interleave.
        super(DIALECT_NAME, PREFIX, PROCESSOR_PRECEDENCE);
    }
    @Override
    public Set<IProcessor> getProcessors(final String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        ... 在这里增加自定义的处理器
        processors.add(new SampleAttributeTagProcessor(dialectPrefix));
        processors.add(new SampleElementTagProcessor(dialectPrefix));
        return processors;
    }
}
  • 自定义定义处理器1:属性处理器
public class SampleAttributeTagProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTR_NAME = "sample1";
    private static final int PRECEDENCE = 10000;

    public SampleAttributeTagProcessor(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 own precedence)
            true);             // Remove the matched attribute afterwards
    }

    @Override
    protected void doProcess(
        final ITemplateContext context, final IProcessableElementTag tag,
        final AttributeName attributeName, final String attributeValue,
        final IElementTagStructureHandler structureHandler) {
        final IEngineConfiguration configuration = context.getConfiguration();
        /*
         * Obtain the Thymeleaf Standard Expression parser
         */
        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
         */
        final Integer position = (Integer) expression.execute(context);
        if(position.equals(1)) {
            structureHandler.setAttribute("style", "background:green");
        } else {
            structureHandler.setAttribute("style", "background:red");
        }
    }
  • 使用定义自定义标签1
<table>
<tr>
    <td>....</td>
    <td wlf:sample1="${user.status}" th:text="${user.status}">状态</td>
    //根据具体的值,改变了td元素的属性值
</tr>
</table>
  • 自定义定义处理器2:元素处理器
public class SampleElementTagProcessor extends AbstractElementTagProcessor {

    private static final String TAG_NAME = "sample3";
    private static final int PRECEDENCE = 1000;

    public Sample3ElementTagProcessor(final String dialectPrefix) {
        super(
            TemplateMode.HTML, // This processor will apply only to HTML mode
            dialectPrefix,     // Prefix to be applied to name for matching
            TAG_NAME,          // Tag name: match specifically this tag
            true,              // Apply dialect prefix to tag name
            null,              // No attribute name: will match by tag name
            false,             // No prefix to be applied to attribute name
            PRECEDENCE);       // Precedence (inside dialect's own precedence)
    }


    @Override
    protected void doProcess(
        final ITemplateContext context, final IProcessableElementTag tag,
        final IElementTagStructureHandler structureHandler) {

        /*
         * Read the 'order' attribute from the tag. This optional attribute in our tag 
         * will allow us to determine whether we want to show a random headline or
         * only the latest one ('latest' is default).
         */
        final String statusValue = tag.getAttributeValue("status");

        final IEngineConfiguration configuration = context.getConfiguration();
        /*
         * Obtain the Thymeleaf Standard Expression parser
         */
        final IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);
        final IStandardExpression expression = parser.parseExpression(context, statusValue);
        final Integer parseStatus = (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();
        if(parseStatus.equals(0)) {
            model.add(modelFactory.createOpenElementTag("td", "style", "background:green"));
            model.add(modelFactory.createText(HtmlEscape.escapeHtml5("停用")));
        }else {
            model.add(modelFactory.createOpenElementTag("td", "style", "background:red"));
            model.add(modelFactory.createText(HtmlEscape.escapeHtml5("启用")));
        }
        model.add(modelFactory.createCloseElementTag("td"));
        /*
         * Instruct the engine to replace this entire element with the specified model.
         */
        structureHandler.replaceWith(model, false);
    }
}
  • 使用定义自定义标签2
<table>
<tr>
    <td>....</td>
    <wlf:sample status="${user.status}"/>
    //根据具体的值,改变了td元素的属性值
</tr>
</table>