You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Freemarker中实现StringUtils.abbreviate等效方法报错求助

修复Freemarker自定义缩略方法中的SimpleScalar转String异常

问题背景

你打算在Freemarker模板中实现类似Apache Commons Lang的StringUtils.abbreviate(String, int)的文本缩略功能,用来生成带字符长度限制的用户专属SMS文本,调用格式类似:

Hello, ${abbrev(userName, 15)}, welcome to the wonderful world of example.com.

你编写了如下自定义方法类:

public class AbbreviateMethod implements TemplateMethodModelEx {
    @Override
    public Object exec(@SuppressWarnings("rawtypes") List arguments) throws TemplateModelException {
        if (arguments.size() < 2) 
            throw new TemplateModelException("Needs 2 parameters");
        return (abbreviate((String) arguments.get(0), ((Integer) arguments.get(1)).intValue()));
    }
}

但运行时抛出了ClassCastException:

java.lang.ClassCastException: freemarker.template.SimpleScalar cannot be cast to java.lang.String
at com.example.util.AbbreviateMethod.exec(AbbreviateMethod.java:26)
at freemarker.core.MethodCall._eval(MethodCall.java:65)
at freemarker.core.Expression.eval(Expression.java:81)
at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:96)
at freemarker.core.DollarVariable.accept(DollarVariable.java:59)
at freemarker.core.Environment.visit(Environment.java:327)
at freemarker.core.Environment.visit(Environment.java:333)
at freemarker.core.Environment.process(Environment.java:306)

问题原因

Freemarker在传递参数给自定义方法时,不会直接传递Java原生类型(如String、Integer),而是用它自己的模板模型类进行包装:

  • 字符串类型会被包装为SimpleScalar(实现了TemplateScalarModel接口)
  • 数字类型会被包装为SimpleNumber(实现了TemplateNumberModel接口)

直接强转成String或Integer自然就会触发ClassCastException。

解决方案

别着急,你需要通过模板模型类提供的方法来获取原生类型的值,同时增加参数类型的校验,避免非法参数导致的异常。以下是修正后的完整代码:

import freemarker.template.*;
import org.apache.commons.lang3.StringUtils; // 假设你用的是Apache Commons Lang的abbreviate方法
import java.util.List;

public class AbbreviateMethod implements TemplateMethodModelEx {

    @Override
    public Object exec(List arguments) throws TemplateModelException {
        // 校验参数数量
        if (arguments.size() < 2) {
            throw new TemplateModelException("Abbreviate method requires 2 parameters: (String, int)");
        }

        // 处理第一个参数:字符串
        Object strArg = arguments.get(0);
        if (!(strArg instanceof TemplateScalarModel)) {
            throw new TemplateModelException("First parameter must be a string");
        }
        TemplateScalarModel scalarModel = (TemplateScalarModel) strArg;
        String inputStr = scalarModel.getAsString();
        // 处理null情况,可根据需求调整返回值
        if (inputStr == null) {
            return "";
        }

        // 处理第二个参数:数字
        Object numArg = arguments.get(1);
        if (!(numArg instanceof TemplateNumberModel)) {
            throw new TemplateModelException("Second parameter must be a number");
        }
        TemplateNumberModel numberModel = (TemplateNumberModel) numArg;
        int maxLength;
        try {
            maxLength = numberModel.getAsNumber().intValue();
        } catch (NumberFormatException e) {
            throw new TemplateModelException("Second parameter must be an integer", e);
        }

        // 调用缩略方法
        return StringUtils.abbreviate(inputStr, maxLength);
    }
}

额外注意事项

  1. 注册自定义方法:记得把这个方法注册到Freemarker的Configuration中,才能在模板里正常使用:
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
    // 其他配置逻辑...
    cfg.setSharedVariable("abbrev", new AbbreviateMethod());
    
  2. 参数合法性校验:新增的类型检查、null值处理让方法更健壮,避免因为非法参数导致的崩溃。
  3. 依赖替代:如果不想依赖Apache Commons Lang库,也可以自己实现文本缩略的逻辑,比如判断字符串长度后截断并添加省略号。

内容的提问来源于stack exchange,提问作者markvgti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:17:02