HOWTO: Use Freemarker, SiteMesh, and Spring MVC together

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

HOWTO: Use Freemarker, SiteMesh, and Spring MVC together

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 /modules/
, and if it's not found there, load it from the classpath package modules.

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

Summary:
* The content of a site-specific web page is written in Freemarker and stored in /WEB-INF/freemarker.
* Site-specific SiteMesh decorators are written in Freemarker and stored in the website's /decorators/
directory. (This could probably be in /WEB-INF/freemarker/decorators)
* Re-usable, default admin screens are written in Freemarker and stored in classpath:modules.
* If needed, a developer can override an admin screen for the site by copying the freemarker file from classpath:modules
into the site's /modules.
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 Reference Guide instructions for using Freemarker in the web tier.
Instead, we have to use Spring's FreeMarkerConfigurationFactoryBean
class to create a custom Freemarker Configuration bean.

applicationContext-freemarker.xml

<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 freemarkerConfiguration
bean above, the important setting is the templateLoaderPaths
.  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 only
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 applicationContext-freemarker.xml
(which loads templates from multiple locations), I needed to extend com.opensymphony.module.sitemesh.freemarker.Freema  rkerDecoratorServlet
.  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 spring-servlet.xml
(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 UrlMapping Bean's mappings 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 spring
):

<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 sitemesh.xml
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 sitemesh-decorators.xml 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!




你可能感兴趣的:(spring,mvc,freemarker,Web,servlet)