freemarker: 实现 数据+模板=输出
其利用ftl模板文件,生成html静态文件。起初认为他生成html后就一直不改变了,这里就一直困惑,既然html静态化了,那么其他人访问时数据改变了,这个html文件不就不能用了吗。最后才发现其每次访问都会重新生成html,然后再访问这个html。
要解决高并发问题,我们只需要把动态网页做成静态网页就可以了,但是问题出来了,如果将所有页面都做成静态页面显然是不切实际的。有什么办法能让我们的网站即能有动态网页的交互性,又有静态网页的加载速度呢?FreeMarker便能实现这样的需求:实现动态网页静态化。
**
总结:freemarker应该对一些实时性要求不太高的页面做静态化处理,且不会每次访问都生成一次html文件。在请求时会判断该html存不存在,然后再做是否生成判断。该结论有待实际项目中验证
freemarker.properties
classic_compatible=true
##如果变量为null,转化为空字符串,比如做比较的时候按照空字符做比较
whitespace_stripping=true
##去掉多余的空格,非常有用
##模版更新事件,设置为1秒,正式环境设置为3600秒
#template_update_delay=3600
template_update_delay=1
##模版更新时间,这里配置是1秒更新一次,正式环境,模版不会改变,可以将这个值设很大,提高效率.就是被这个参数害死了.老是不更新.
locale=zh_CN
##中国
default_encoding=utf-8
##编码utf8
url_escaping_charset=utf-8
##url编码utf8
date_format=yyyy-MM-dd
##显示日期格式
time_format=HH:mm:Ss
##显示时间格式
datetime_format=yyyy-MM-dd
##显示日期格式
number_format=\#0.\#\#\#\#\#
##数字显示格式hi.
#template_update_delay=1
datetime_format=yyyy-MM-dd HH:mm:ss
date_format=yyyy-MM-dd
time_format=HH:mm:ss
number_format=0.######;
boolean_format=true,false
##auto_import="/common/index.ftl" as ui
whitespace_stripping=true
default_encoding=UTF-8
tag_syntax=auto_detect
##设置标签的类型
url_escaping_charset=UTF-8
application-freemarker.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 配置Freemarker属性文件路径 -->
<bean id="freemarkerConfiguration" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="3" />
<property name="location">
<value>/WEB-INF/config/freemarker.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<!-- 配置freeMarker模板加载地址 -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 视图解析器会在/WEB-INF/ftl/路径下扫描视图文件 -->
<property name="templateLoaderPath" value="/templates/ftl/" />
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">0</prop>
<prop key="default_encoding">${default_encoding}</prop>
<prop key="datetime_format">${datetime_format}</prop>
<prop key="locale">${locale}</prop>
</props>
</property>
<!-- <property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape" />
</map>
</property> -->
</bean>
<!-- escapeXml方法过滤特殊字符 -->
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape" />
<!-- 配置freeMarker视图解析器 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<!-- <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> -->
<property name="viewClass" value="com.general.common.freemarker.MyFreeMarkerView" />
<property name="order" value="0"></property>
<property name="cache" value="false"></property>
<property name="suffix" value=".ftl" />
<property name="contentType" value="text/html;charset=UTF-8" />
<!-- 此变量值为pageContext.request, 页面使用方法:rc.contextPath -->
<property name="requestContextAttribute" value="rc" />
</bean>
</beans>
重写了视图解析器vieclass
package com.controller.freemarker;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class MyFreeMarkerView extends FreeMarkerView {
@Override
protected void doRender(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Expose model to JSP tags (as request attributes).
exposeModelAsRequestAttributes(model, request);
// Expose all standard FreeMarker hash models.
SimpleHash fmModel = buildTemplateModel(model, request, response);
if (logger.isDebugEnabled()) {
logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale(request);
/*
* 默认生成静态文件,除非在编写ModelAndView时指定CREATE_HTML = false,
* 这样对静态文件生成的粒度控制更细一点
* 例如:ModelAndView mav = new ModelAndView("search");
* mav.addObject("CREATE_HTML", false);
*/
if(Boolean.FALSE.equals(model.get("CREATE_HTML"))){
processTemplate(getTemplate(locale), fmModel, response);
}else{
createHTML(getTemplate(locale), fmModel, request, response);
}
}
public void createHTML(Template template, SimpleHash model,HttpServletRequest request,
HttpServletResponse response) throws IOException, TemplateException, ServletException {
//站点根目录的绝对路径
String basePath = request.getSession().getServletContext().getRealPath("/");
String requestHTML = this.getRequestHTML(request);
//静态页面绝对路径
/*String htmlPath = basePath + requestHTML; */
String htmlPath = basePath +"templates"+ requestHTML;
File htmlFile = new File(htmlPath);
if(!htmlFile.getParentFile().exists()){
htmlFile.getParentFile().mkdirs();
}
if(!htmlFile.exists()){
htmlFile.createNewFile();
}
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(htmlFile), "UTF-8"));
//处理模版
template.process(model, out);
out.flush();
out.close();
/*将请求转发到生成的htm文件*/
/*request.getRequestDispatcher(requestHTML).forward(request, response);*/
request.getRequestDispatcher("/templates/"+requestHTML).forward(request, response);
}
/**
* 计算要生成的静态文件相对路径
* 因为大家在调试的时候一般在Tomcat的webapps下面新建站点目录的,
* 但在实际应用时直接布署到ROOT目录里面,这里要保证路径的一致性。
* @param request HttpServletRequest
* @return /目录/*.htm
*/
private String getRequestHTML(HttpServletRequest request){
//web应用名称,部署在ROOT目录时为空
String contextPath = request.getContextPath();
//web应用/目录/文件.do
String requestURI = request.getRequestURI();
//basePath里面已经有了web应用名称,所以直接把它replace掉,以免重复
requestURI = requestURI.replaceFirst(contextPath, "");
//将.do改为.htm,稍后将请求转发到此htm文件
/*requestURI = requestURI.substring(0, requestURI.indexOf(".")) + ".htm";*/
requestURI = requestURI+ ".html";
return requestURI;
}
}
@RequestMapping("/map")
public ModelAndView map1(HttpServletRequest request, HttpServletResponse response) {
List<Xiaoqu> xiaoqulist = new ArrayList<Xiaoqu>();
double [] longitude = {117.296979,116.8198,119.011435,115.813411};
double [] latitude = {31.839387,31.375842,29.860429,32.300805};
String [] name = {"\'名城\'","\'尚贤居\'","\'复兴家园\'","\'永和村\'"};
String [] count = {"103","203","502","401"};
try {
for(int i=0;i<4;i++){
Xiaoqu xq = new Xiaoqu();
xq.setLongitude(longitude[i]);
xq.setLatitude(latitude[i]);
xq.setCount(count[i]);
xq.setName(name[i]);
xiaoqulist.add(xq);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new ModelAndView("map", "xiaoqulist", xiaoqulist);
}
@RequestMapping("/map2")
public ModelAndView map2(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
//在这里可以控制不生成静态htm
mv.addObject("CREATE_HTML", false);
mv.setViewName("index");
return mv;
}
注:之前生成html 文件一直访问不到,因为spring 对静态文件做了拦截
web.xml添加:
<!-- freemarker 生成的html静态文件 -->
<servlet-mapping >
<servlet-name >default</servlet-name >
<url-pattern >*.html</url-pattern>
</servlet-mapping >