在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); } }
额外注意事项
- 注册自定义方法:记得把这个方法注册到Freemarker的
Configuration中,才能在模板里正常使用:Configuration cfg = new Configuration(Configuration.VERSION_2_3_32); // 其他配置逻辑... cfg.setSharedVariable("abbrev", new AbbreviateMethod()); - 参数合法性校验:新增的类型检查、null值处理让方法更健壮,避免因为非法参数导致的崩溃。
- 依赖替代:如果不想依赖Apache Commons Lang库,也可以自己实现文本缩略的逻辑,比如判断字符串长度后截断并添加省略号。
内容的提问来源于stack exchange,提问作者markvgti
相关产品推荐
相关产品推荐

