春天 + 瓷砖2 + Freemarker - 通过 Freemarker Servlet 或 Spring 的 FreeMarkerViewResolver 集成?

发布于 2024-07-08 01:12:43 字数 1585 浏览 8 评论 0原文

今天我在尝试从 Freemarker 迁移到 Tiles2 + Freemarker 时遇到了困难。

我的 freemarker 模板使用来自 spring.ftl 的宏。

如果我在 web.xml 中提供 fremarker servlet,我的模型对 freemarker 是可见的,但特定的 spring 变量(自然)不会填充到模型中,因为 springs FreemarkerView 负责这一点。

如果我为特定 url 配置单独的 DispatcherServlet(例如“/tpl/*”)并将 freemarker 解析器配置为该 servlet 的默认视图解析器,并提供 UrlFilenameViewController 作为默认控制器,则特殊的 spring 变量确实会填充到模型中,但我自己的模型是不可见:它被绑定为请求属性。 我可以通过 ${Request.mymodel.myvar} 访问我的模型,但这样我就必须更改我所有的 freemarker 模板,并且我发现这个想法有些奇怪。

现在我的解决方案是扩展 UrlFilenameViewController 并将我的模型从请求添加到 ModelAndView:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)  {
        ModelAndView mav = super.handleRequestInternal(request, response);

        HashMap<String, Object> map = new HashMap<String, Object>();

        Enumeration<String> attributes = request.getAttributeNames();

        while(attributes.hasMoreElements()) {
            String attribute = attributes.nextElement();

            if("model".equals(attribute)) {
                logger.debug("FreemarkerViewController.handleRequestInternal: putting attribute to model: " + attribute + "=" + request.getAttribute(attribute));
                map.put(attribute, request.getAttribute(attribute));
            }
        }
        logger.debug("FreemarkerViewController.handleRequestInternal: VIEW: " + mav.getViewName());
        return new ModelAndView(mav.getViewName(), map);
    }

但是这个解决方案也有点臭 - 如果我向业务控制器中的模型添加一些内容,我必须将其添加到此处。

对于我的问题有一个优雅的解决方案吗?

I was strugling today trying to migrate from Freemarker to Tiles2 + Freemarker.

My freemarker templates use macros that come from spring.ftl.

If I provide a fremarker servlet in web.xml, my model is visible to freemarker, but specific spring variables (naturally) are not populated into the model as springs FreemarkerView is responsible for that.

If I configure a separate DispatcherServlet for specific url (say "/tpl/*") and configure freemarker resolver as default view resolver for that servlet and provide UrlFilenameViewController as default controller, special spring variables do get populated to model, but my own model is not visible: it is bound as a request attribute. I can access my model via ${Request.mymodel.myvar} but this way I have to change all my freemarker templates and I see something smelly in the idea.

Now my solution was to extend UrlFilenameViewController and add my model from request to ModelAndView:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)  {
        ModelAndView mav = super.handleRequestInternal(request, response);

        HashMap<String, Object> map = new HashMap<String, Object>();

        Enumeration<String> attributes = request.getAttributeNames();

        while(attributes.hasMoreElements()) {
            String attribute = attributes.nextElement();

            if("model".equals(attribute)) {
                logger.debug("FreemarkerViewController.handleRequestInternal: putting attribute to model: " + attribute + "=" + request.getAttribute(attribute));
                map.put(attribute, request.getAttribute(attribute));
            }
        }
        logger.debug("FreemarkerViewController.handleRequestInternal: VIEW: " + mav.getViewName());
        return new ModelAndView(mav.getViewName(), map);
    }

But this solution is somewhat smelly too - if I add something to the model in my business controllers, I have to add it here.

Is there an elegant solution for my problem?

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

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

发布评论

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

评论(1

浅唱ヾ落雨殇 2024-07-15 01:12:43

我记得我在两个项目中解决了同样的问题。
您的第二种方法几乎是正确的(FreeMarkerViewResolver),但如果我没记错的话,我还必须从 FreeMarkerViewTilesView 扩展以显式桥接这两个模型一起。

自定义 Tiles 视图:

public class CustomTilesView extends TilesView {

    @Override
    protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) {
        request.setAttribute(CustomFreeMarkerView.MODEL_KEY, model);
    }
}

自定义 FreeMarker 视图:

public class CustomFreeMarkerView extends FreeMarkerView {

    public static final String MODEL_KEY = FreeMarkerView.class.getName() + ".MODEL";

    @Override
    protected void exposeHelpers(Map model, HttpServletRequest request) throws Exception {
        super.exposeHelpers(model, request);
        final Map savedModel = (Map) request.getAttribute(MODEL_KEY);
        if (savedModel != null) {
            mergeModels(model, savedModel);
        }
    }

    private void mergeModels(Map<String, Object> targetModel, Map<String, Object> recipientModel) throws ServletException {
        for (Map.Entry<String, Object> entry : recipientModel.entrySet()) {
            String key = entry.getKey();
            if (targetModel.containsKey(key)) {
                throw new ServletException("Cannot merge models because of an existing model object of the same name: " + key);
            }
            targetModel.put(key, entry.getValue());
        }
    }
}

在 Spring 中注册:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"
      p:viewClass="com.my.CustomTilesView"
      p:contentType="text/html;charset=UTF-8"/>

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
      p:suffix=".ftl"
      p:exposeSpringMacroHelpers="true"
      p:viewClass="com.my.CustomFreeMarkerView"
      p:contentType="text/html;charset=UTF-8"/>

应该可以。

I remember I solved the same problem in two projects.
Your second approach is almost right (FreeMarkerViewResolver), but if I remember correctly I also had to extend from FreeMarkerView and TilesView to explicitly bridge both models together.

Custom Tiles view:

public class CustomTilesView extends TilesView {

    @Override
    protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) {
        request.setAttribute(CustomFreeMarkerView.MODEL_KEY, model);
    }
}

Custom FreeMarker view:

public class CustomFreeMarkerView extends FreeMarkerView {

    public static final String MODEL_KEY = FreeMarkerView.class.getName() + ".MODEL";

    @Override
    protected void exposeHelpers(Map model, HttpServletRequest request) throws Exception {
        super.exposeHelpers(model, request);
        final Map savedModel = (Map) request.getAttribute(MODEL_KEY);
        if (savedModel != null) {
            mergeModels(model, savedModel);
        }
    }

    private void mergeModels(Map<String, Object> targetModel, Map<String, Object> recipientModel) throws ServletException {
        for (Map.Entry<String, Object> entry : recipientModel.entrySet()) {
            String key = entry.getKey();
            if (targetModel.containsKey(key)) {
                throw new ServletException("Cannot merge models because of an existing model object of the same name: " + key);
            }
            targetModel.put(key, entry.getValue());
        }
    }
}

Register both in Spring:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"
      p:viewClass="com.my.CustomTilesView"
      p:contentType="text/html;charset=UTF-8"/>

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
      p:suffix=".ftl"
      p:exposeSpringMacroHelpers="true"
      p:viewClass="com.my.CustomFreeMarkerView"
      p:contentType="text/html;charset=UTF-8"/>

Should work.

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