将 Apache Velocity 与 StringBuilders/CharSequences 结合使用

发布于 2024-10-20 19:19:38 字数 785 浏览 6 评论 0原文

我们使用 Apache Velocity 来创建动态模板。目前 Velocity 有以下评估/替换方法:

public static boolean evaluate(Context context, Writer writer, String logTag, Reader reader)

public static boolean evaluate(Context context, Writer out, String logTag, String instring)

我们通过提供 StringWriter 来使用这些方法来写入评估结果。我们传入的数据采用 StringBuilder 格式,因此我们使用 StringBuilder.toString 并将其作为 instring 提供。

问题是我们的模板相当大(可以是兆字节,极少数情况下可达数十毫秒),替换发生得非常频繁,并且每次替换操作三倍所需的内存量(传入数据+ StringBuilder.toString() 创建一个新的副本+传出数据)。

我想知道是否有办法改善这一点。例如,如果我能找到一种方法在同一 StringBuilder 实例之上提供 ReaderWriter ,该实例仅使用额外的内存来处理输入/输出差异,这是一个好方法吗?有没有人做过类似的事情并且可以分享此类课程的任何来源?或者对于给定的问题可能有更好的解决方案吗?

We are using Apache Velocity for dynamic templates. At the moment Velocity has following methods for evaluation/replacing:

public static boolean evaluate(Context context, Writer writer, String logTag, Reader reader)

public static boolean evaluate(Context context, Writer out, String logTag, String instring)

We use these methods by providing StringWriter to write evaluation results. Our incoming data is coming in StringBuilder format so we use StringBuilder.toString and feed it as instring.

The problem is that our templates are fairly large (can be megabytes, tens of Ms on rare cases), replacements occur very frequently and each replacement operation triples the amount of required memory (incoming data + StringBuilder.toString() which creates a new copy + outgoing data).

I was wondering if there is a way to improve this. E.g. if I could find a way to provide a Reader and Writer on top of same StringBuilder instance that only uses extra memory for in/out differences, would that be a good approach? Has anybody done anything similar and could share any source for such a class? Or maybe there any better solutions to given problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

哽咽笑 2024-10-27 19:19:38

Velocity 需要先解析整个模板,然后才能对其进行评估。您将无法提供 ReaderWriter 来在单次评估中获得任何结果。不过,您可以将模板分成更小的部分来单独评估它们。这将取决于它们的内容以及各个部分是否相互依赖。根据您的情况,开销可能不值得。

如果您只处理模板中的变量替换,您可以简单地评估输入的每一行。理想情况下,您可以在它进入 StringBuilder 之前拦截它。否则,您仍然需要承担该内存及其 toString() 的成本,您将其输入到 BufferedReader 中以进行 readLine() 呼吁反对。

如果有 #set 指令,您需要继续传递相同的上下文进行评估。如果有任何 #if#foreach 块,事情就会变得棘手。实际上,我之前已经这样做过,并读取了足够多的行来捕获输入块,以便 Velocity 进行解析和评估。然而,到那时你就开始做 Velocity 的工作了,这可能不值得。

Velocity needs to parse the whole template before it can be evaluated. You won't be able to provide a Reader and Writer to gain anything in a single evaluation. You could however break up your templates into smaller parts to evaluate them individually. That's going to depend on what's in them and if the parts would depend on each other. And the overhead might not be worth it, depending on your situation.

If you're only dealing with variable substitution in your templates you could simply evaluate each line of your input. Ideally you can intercept that before it goes into the StringBuilder. Otherwise you're still going to have to incur the cost of that memory plus its toString() that you'd feed into a BufferedReader to make readLine() calls against.

If there are #set directives you'll need to keep passing the same context for evaluation. If there are any #if or #foreach blocks it's going to get tricky. I have actually done this before and read in enough lines to capture the block of input for Velocity to parse and evaluate. At that point however you're starting to do Velocity's job and it's probably not worth it.

腹黑女流氓 2024-10-27 19:19:38

您可以通过反射从 StringBuilder 读取 value 字段并在其上创建一个 CharArrayReader 来保存字符串的一份副本:

    StringBuilder sb = new StringBuilder("bla");
    Field valueField = StringBuilder.class.getSuperclass().getDeclaredField("value");
    valueField.setAccessible(true);
    char[] value = (char[]) valueField.get(sb);
    Reader r = new CharArrayReader(value, 0, sb.length());

You can save one copy of the string by reading the value field from the StringBuilder through reflection and creating a CharArrayReader on that:

    StringBuilder sb = new StringBuilder("bla");
    Field valueField = StringBuilder.class.getSuperclass().getDeclaredField("value");
    valueField.setAccessible(true);
    char[] value = (char[]) valueField.get(sb);
    Reader r = new CharArrayReader(value, 0, sb.length());
别低头,皇冠会掉 2024-10-27 19:19:38

哎呀。这是评估()的一个相当重量级的用途。我认为您有充分的理由不使用标准资源加载器,所以我不会武断。 :)

我还没有听说过任何适合这个的解决方案,但由于 Reader 不是一个特别复杂的类,我的直觉是创建您自己的 StringBufferReader 类并将其传入。

Yikes. That's a pretty heavyweight use for evaluate(). I assume you have good reasons for not using the standard resource loader stuff, so i won't pontificate. :)

I haven't heard of any solution that would fit this, but since Reader is not a particularly complicated class, my instinct would be to just create your own StringBufferReader class and pass that in.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文