HOWTO: Use Freemarker, SiteMesh, and Spring MVC together

转自[url]http://forum.springsource.org/showthread.php?t=53476[/url]

[b]HOWTO: Use Freemarker, SiteMesh, and Spring MVC together[/b]

I hope this thread will help others who are trying to combine these
technologies. After much pain, I finally got it working exactly the way
I want.

My goal was to build a website using Freemarker for templating,
SiteMesh for layouts, and Spring MVC. Normally you would just put the
Freemarker files in WEB-INF/freemarker.

However, this site is one of many we build that re-use the same Content
Management Admin screens - so we wanted the Freemarker templates for
those forms in the classpath. But we sometimes need to override those
admin screens with customizations in a specific project. To enable
customizations, we want to be able to first try to load the Freemarker
template from the WAR file in [b]/modules/[/b]
, and if it's not found there, load it from the classpath package [b]modules[/b].

Additionally, each website will have its site-specific HTML content written in Freemarker - these files be stored in [b]/WEB-INF/freemarker[/b].

Summary:
* The content of a site-specific web page is written in Freemarker and stored in [b]/WEB-INF/freemarker[/b].
* Site-specific SiteMesh decorators are written in Freemarker and stored in the website's [b]/decorators/[/b]
directory. (This could probably be in /WEB-INF/freemarker/decorators)
* Re-usable, default admin screens are written in Freemarker and stored in [b]classpath:modules[/b].
* If needed, a developer can override an admin screen for the site by copying the freemarker file from [b]classpath:modules[/b]
into the site's [b]/modules.[/b]
folder. Then they can make any customizations needed.

We also use Freemarker in the service tier to generate emails, so we can't just follow the [url=http://static.springframework.org/spring/docs/2.5.x/reference/view.html#view-velocity-contextconfig]Reference Guide instructions for using Freemarker in the web tier[/url].
Instead, we have to use Spring's [b]FreeMarkerConfigurationFactoryBean[/b]
class to create a custom Freemarker [b]Configuration[/b] bean.

[b]applicationContext-freemarker.xml[/b]

<bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<description>Using the Config directly so we can use it outside the web tier</description>
<property name="templateLoaderPaths">
<list>
<value>/WEB-INF/freemarker/</value>
<value>/modules/</value>
<value>/</value>
<value>classpath:modules</value>
<value>classpath:org/springframework/web/servlet/view/freemarker</value>
</list>
</property>
<property name="freemarkerSettings">
<props>
<prop key="datetime_format">MM/dd/yyyy</prop>
<prop key="number_format">#</prop>
<prop key="whitespace_stripping">true</prop>
</props>
</property>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape" />
<entry key="html_escape" value-ref="fmHtmlEscape" />
</map>
</property>
</bean>

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<description>Required for Freemarker to work in web tier</description>
<property name="configuration" ref="freemarkerConfiguration" />
</bean>

<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape" />

<bean id="fmHtmlEscape" class="freemarker.template.utility.HtmlEscape" />


In the [b]freemarkerConfiguration[/b]
bean above, the important setting is the [b]templateLoaderPaths[/b]
. When Freemarker tries to find a template file, it will look for the template in the following order:

* /WEB-INF/freemarker

* /modules/

* / (the site's root - important for decorators)

* The modules package in the classpath - this could be a JAR file in WEB-INF/lib or in WEB-INF/classes

* The org/springframework/web/servlet/view/freemarker package on the
classpath - this is so we can reuse Spring's Freemarker Macros

By Default, The Freemarker Servlet creates a new Configuration object, which will [b]only[/b]
load templates from the Servlet Context, a physical directory, OR a
classpath location (in other words, it does not use
freemarker.cache.MultiTemplateLoader). SiteMesh extends the
FreemarkerServlet to do its decorating. Since we need to re-use the
Configuration created in [b]applicationContext-freemarker.xml[/b]
(which loads templates from multiple locations), I needed to extend [b]com.opensymphony.module.sitemesh.freemarker.Freema rkerDecoratorServlet[/b]
. I wrote the following Subclass:

package com.kazaam.sitemesh.servlet;

import java.io.IOException;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet;

import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;

/**
* @author mstralka
*
*/
public class KzSpringFreemarkerDecoratorServlet extends FreemarkerDecoratorServlet {

@Override
public void init() throws ServletException {
super.init();
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
Configuration springConfiguration = (Configuration) ctx.getBean("freemarkerConfiguration");
TemplateLoader templateLoader = springConfiguration.getTemplateLoader();
getConfiguration().setTemplateLoader(templateLoader);
}
}


Normally, The SiteMesh FreemarkerDecoratorServlet is configured
in web.xml. Since we want to re-use our Spring Interceptors, we are
going to use Spring's ServletWrappingController to configure the
servlet as a Spring bean instead.

In [b]spring-servlet.xml[/b]
(or whatever you call your Spring MVC XML file), add the following bean (which wraps the Servlet in a controller so we can re-use our Spring MVC interceptors for Hibernate, etc):

<bean id="freemarkerWrapperServletController" class="org.springframework.web.servlet.mvc.ServletWrappingController">
<property name="servletClass" value="com.kazaam.sitemesh.servlet.KzSpringFreemarkerDecoratorServlet" />
<property name="servletName" value="sitemesh-freemarker" />
<property name="initParameters">
<props>
<prop key="TemplatePath">/</prop><!--this is ignored by our custom implementation but keep it here-->
<prop key="default_encoding">ISO-8859-1</prop>
</props>
</property>
</bean>


Add the following to your [b]UrlMapping[/b] Bean's [b]mappings[/b] property:

<prop key="/**/*.ftl">freemarkerWrapperServletController</prop>
<prop key="/**/*.ftd">freemarkerWrapperServletController</prop>


In web.xml, make sure *.ftl and *.ftd are handled by your Spring dispatcher servlet (my dispatcher servlet is named [b]spring[/b]
):

<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


In addition to whatever other URL patterns you let Spring handle,add the following so the Freemarker templates are handled by Spring too:

<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.ftd</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>


To complete the SiteMesh configuration, here is my [b]sitemesh.xml[/b]
file in the /WEB-INF/ directory (Note that I name my decorators.xml
file sitemesh-decorators.xml file instead because I like them to appear
next to each other alphabetically in Eclipse)

<sitemesh>
<property name="decorators-file" value="/WEB-INF/sitemesh-decorators.xml" />
<excludes file="/WEB-INF/sitemesh-decorators.xml" />

<page-parsers>
<parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
<parser content-type="text/html;charset=ISO-8859-1" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
</page-parsers>

<decorator-mappers>
<mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
<param name="property.1" value="meta.decorator" />
<param name="property.2" value="decorator" />
</mapper>
<mapper class="com.opensymphony.module.sitemesh.mapper.FrameSetDecoratorMapper"/>
<mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
<param name="decorator" value="printable" />
<param name="parameter.name" value="printable" />
<param name="parameter.value" value="true" />
</mapper>
<mapper class="com.opensymphony.module.sitemesh.mapper.FileDecoratorMapper"/>
<mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
<param name="config" value="/WEB-INF/sitemesh-decorators.xml" />
</mapper>
</decorator-mappers>
</sitemesh>


And here is my [b]sitemesh-decorators.xml[/b] file (yours will be different):

<decorators defaultdir="/decorators">
<!-- SiteMesh Decorator Mappings:
See http://opensymphony.com/sitemesh/dm.html
-->

<!-- No decorator -->
<excludes>
<pattern>/misc/*</pattern>
<pattern>/dwr/*</pattern>
<pattern>/articles/rss.*</pattern>
<pattern>/dwr/index.html</pattern>
</excludes>

<decorator name="main" page="main.ftd">
<url-pattern>*</url-pattern>
</decorator>

<decorator name="authoring" page="authoring.ftd">
<url-pattern>/page/create*</url-pattern>
<url-pattern>/page/edit*</url-pattern>
<url-pattern>/article/create*</url-pattern>
<url-pattern>/article/edit*</url-pattern>
</decorator>

<decorator name="popup" page="popup.ftd">
<url-pattern>/page/wikihelp*</url-pattern>
<url-pattern>/page/help*</url-pattern>
</decorator>

</decorators>


Hope this helps!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值