Spring + Tiles2 + Freemarker - integrate via Freemarker Servlet or via Spring's FreeMarkerViewResolver?

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?

Asked by: Jack132 | Posted: 28-01-2022

Answer 1

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 {

    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";

    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"

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"

Should work.

Answered by: Edward661 | Posted: 01-03-2022

Similar questions

Java FreeMarkerViewResolver and plain html view

I'm having some trouble trying to have both a simple static plain html and a ftl with freemarker working at the same time. I have Spring 4.0.2.RELEASE and the following web configuration @Configuration @EnableWebMvc @EnableAspectJAutoProxy @ComponentScan({"com.test.myproject"}) @ImportResource("classpath:application-context.xml") public class TrackerWebConfig extends WebMvcConfigurerAdapter { ...

Still can't find your answer? Check out these amazing Java communities for help...

Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)