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>

你可能感兴趣的:(java)