CMS使用freemarker实现页面静态化

本文探讨了FreeMarker作为页面静态化工具的优势,包括它能彻底分离表现层和业务逻辑,提高开发效率,以及使开发人员分工更明确。然而,FreeMarker也存在缺点,如模板修改后可能出现过期数据、必须为变量赋值、Map限定key为字符串以及不支持集群应用。文章通过配置pom文件和展示后台及模板源码,提供了实际应用的指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一) FreeMarker的优点

  1. 可以彻底的分离表现层和业务逻辑。曾经在使用JSP 开发过程中发现在页面中大量的存在业务逻辑的代码,使得页面内容凌乱,在后期大量的修改维护过程中就变得非常困难。但是,**FreeMarker不支持Java脚本代码。**FreeMarker的原理就是:模板+数据模型=输出,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,而所有的逻辑都是由数据模型来处理的。用户最终看到的输出是模板和数据模型合并后创建的。

2.**FreeMarker可以提高开发效率。**根据以往的开发经验,使用的都是JSP 页面来展示数据的,即所谓的表现层。大家都知道,JSP在第一次执行的时候需要转换成Servlet类,开发阶段进行功能调适时,需要频繁的修改JSP,每次修改都要编译和转换,那么试想一天中我们浪费在程序编译的时间有多少。相对于JSP来说,FreeMarker模板技术不存在编译和转换的问题,所以就不会存在上述问题。而且开发过程中,大家在不必在等待界面设计开发人员完成页面原形后,大家再来开发程序。

3 . **FreeMarker使得开发过程中的人员分工更加明确。**以往用JSP展现数据时,作为程序员的我们,并不熟悉界面设计技术,反之界面开发人员,也并不熟悉程序语言。对两者而言,交替性的工作本身就有难度。有时候稍有不慎,可能会将某个页面元素删除或去掉了某个程序符号,使得页面走样或程序错误,这样就需要双方相互沟通协作,解决出现的问题。有时候因为项目中的时间,任务量等因素的存在,可能这个工作就由一个人来完成,这样就可能加大某一方开发人员的工作量。使用FreeMarker后,作为界面开发人员,只专心创建HTML文件、图像以及Web页面的其他可视化方面,不用理会数据;而程序开发人员则专注于系统实现,负责为页面准备要显示的数据。

(二)FreeMarker的缺点
1.FreeMarker模板技术,在修改模板后,可能会看到已经过期的数据。如:生成静态的HTML页面后,如果一旦模板改变,而没有及时更新模板生成的HTML页面的话,用户看到的就是过期的数据。

2.FreeMarker模板技术在应用过程中,FreeMarker中的变量必须要赋值,如果不赋值,那么就会抛出异常。想避免错误就要应用if/elseif/else 指令进行判段,如果对每一个变量都判断的话,那么则反而增加了编程的麻烦。

3.FreeMarker的map限定key必须是string,其他数据类型无法操作。

4.FreeMarker不支持集群应用。为了编成的方便性,把序列化的东西都放到了Session中,如Session,request等,在开发的过程中确实方便,但如果将应用放到集群中,就会出现错误。

(三)配置pom文件,引入所需jar包

<!-- FreeMarker -->
<dependency>
	<groupId>org.freemarker</groupId>
	<artifactId>freemarker-gae</artifactId>
	<version>2.3.25-incubating</version>
</dependency>

(四)后台源码

/**
 * 首页静态化
 * 首页有人访问直接生成html文件,如果存在则直接访问
 */
@Controller
@RequestMapping
public class index extends HttpServlet {
	private static final long serialVersionUID = 1L;
	@Autowired
	private ChannelService channelService;
	@Autowired
	private ContentService contentService;
	@Autowired
	private AdvertisementService advertisementService;

	@RequestMapping
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doGet(request, response);
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html; charset=UTF-8");
		String saveDir = request.getServletContext().getRealPath(FreeMarkerConstants.index_dir);
		String indexPath = request.getServletContext()
				.getRealPath(FreeMarkerConstants.index_dir + FreeMarkerConstants.index_html);
		File file = new File(saveDir);
		if (!file.exists()) {
			file.mkdirs();
		}

		file = new File(indexPath);
		if (!file.exists()) {

			Configuration configuration = new Configuration(Configuration.VERSION_2_3_22);
			String templatePath = this.getClass().getClassLoader().getResource("/templates/index").getPath();
			configuration.setDirectoryForTemplateLoading(new File(templatePath));
			configuration.setDefaultEncoding("UTF-8");

			Map<String, Object> dataMap = new HashMap<String, Object>();
			List<Advertisement> advertisements = this.getAdvertisements();
			List<Channel> praentChannelList = this.getData();
			List<Content> stickContent = this.getStickContent();
			List<Content> subCourseContent = this.getStickCourseCourse();
			List<Content> stickExpertContent = this.getStickExpertContent();
			List<Content> stickActivityContent = this.getStickActivityContent();
			List<Content> stickGeneralContent = this.getStickGeneralContent();
			base ctx = this.initPath(request, response);

			dataMap.put("advertisements", advertisements);
			dataMap.put("praentChannelList", praentChannelList);
			dataMap.put("stickContent", stickContent);
			dataMap.put("subCourseContent", subCourseContent);
			dataMap.put("stickExpertContent", stickExpertContent);
			dataMap.put("stickActivityContent", stickActivityContent);
			dataMap.put("stickGeneralContent", stickGeneralContent);
			dataMap.put("ctx", ctx);

			
			Template template = configuration.getTemplate("index.ftl");

			try {
				
				Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
				template.process(dataMap, writer);
				writer.flush();
				writer.close();
			} catch (TemplateException e) {
				e.printStackTrace();
			}
		}
		request.getRequestDispatcher(FreeMarkerConstants.index_dir + FreeMarkerConstants.index_html).forward(request,response);
	}


	/** basepath */
	@ModelAttribute
	public base initPath(HttpServletRequest request, HttpServletResponse response) {
		base ctx = new base();
		String path = request.getContextPath();
		String basepath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path;
		ctx.setBasepath(basepath);
		return ctx;
	}
}

(五)模板源码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>XXX网_新XX_新XX_新XX</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=2.0,minimum-scale=0.5,user-scalable=no"/>
    <meta name="description" content="XXX网专注新课程理念与实践,为教育工作者提供关于XXX ADN 教师研修和新教学的最前沿资讯、专业发展资源和教育服务">
    <meta name="keyword" content="THIS IS A KEY WERDS">
    <meta name="baidu-site-verification" content="oq8mUxZ2YI" />
    <link href="${ctx.basepath}/resources/front/img/gf_favicon.ico" rel="shortcut icon" />

    <link rel="stylesheet" href="${ctx.basepath}/resources/front/css/bootstrap.min.css">
    <#--轮播图-->
    <link rel="stylesheet" href="${ctx.basepath}/resources/front/css/swiper.min.css">
    <#--返回顶部-->
    <link rel="stylesheet" href="${ctx.basepath}/resources/front/css/goTop.css">
    <link rel="stylesheet" href="${ctx.basepath}/resources/front/css/d-style.css">
    <script src="${ctx.basepath}/resources/front/js/jquery-3.2.1.min.js"></script>
    <script src="${ctx.basepath}/resources/front/js/bootstrap.min.js"></script>
    <#--轮播图-->
    <script src="${ctx.basepath}/resources/front/js/swiper.min.js"></script>
    <#--锚点导航-->
    <script src="${ctx.basepath}/resources/front/js/nav.js"></script>
    <#--返回顶部-->
    <script src="${ctx.basepath}/resources/front/js/goTop.js"></script>
</head>
<body>
    <#--顶部-->
    <div class="d-top">
        <div class="d-nav-fixed">
            <!--顶部-->
            <div class="d-nav-topW">
                <div class="container d-nav-top">
                    <img src="${ctx.basepath}/resources/front/img/nav-top.png" alt="">
                    <p class="d-nav-top-phone">010-89940851</p>
                </div>
            </div>
            <#--顶部导航-->
            <div class="d-nav-new-Wrap">
                <div class="container">
                    <a class="d-logo-text"><img src="${ctx.basepath}/resources/front/img/d-logo-text.png" alt=""></a>
                    <ul class="d-nav-new">
                        <li><a href="#d_observe_anchor">New Course</a></li>
                        <li><a href="#d_visual_anchor">Expert</a></li>
                        <li><a href="#d_service_anchor">Server</a></li>
                        <li><a href="#d_fine_anchor">精品课程</a></li>
                        <li><a class="active" href="#d_training_anchor">研修活动</a></li>
                        <#--跳转的时候用事件跳转-->
                        <li class="d-about-us-nav"><span>关于我们</span></li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="d-nav-fixed-seat"></div>
        <#--轮播-->
    	<div class="d-swiper-wrap">
            <div class="swiper-container">
                <div class="swiper-wrapper">
                	<#list advertisements as advertisement>
	                    <div class="swiper-slide">
	                        <#if (advertisement.url)??>
	                        	<a target="_blank" href="${advertisement.link }">
	                        	<div class="swiper-slide-img" style="background: url(${ctx.basepath}${advertisement.url }) no-repeat center;"></div>
	                    		</a>
	                    		<#else>
	                    		<div class="swiper-slide-img" style="background: url(${ctx.basepath}/resources/front/img/banner3.png) no-repeat center;"></div>
	                    	</#if>
	                    </div>
                   </#list>
                </div>
                <#-- 如果需要分页器 -->
                <div class="swiper-pagination"></div>
                <#-- 如果需要导航按钮 -->
                <div class="swiper-button-prev"></div>
                <div class="swiper-button-next"></div>

            </div>
        </div>
	</div>
    <#--中间内容-->
    <#--New Course-->
    <div class="container clearfix" id="d_observe_anchor">
        <h2 class="d-title">
        	<#list praentChannelList as praent>
    			<#if praent.channelId==1>
			        	<b>${praent.channelName }</b>
			        	<a target="_blank" href="/xkcgc.html" class="d-more">MORE</a>
	        	</#if>
	        </#list>
        </h2>

        <div class="row">
        	<#list stickContent as stick>
        		<#if stick_index<3>
		            <div class="col-md-4">
		                <a target="_blank" href="/xkcgc/${stick.contentId}.html" class="d-observeWrap">
		                    <div class="d-observe-img">
		                    	<#if (stick.picture)??>
	                    			<img src="${stick.picture }" alt="">
		                    		<#else>
		                    			<img src="${ctx.basepath}/resources/front/img/observe.png" alt="">
		                    	</#if>
		                    </div>
		                    <h3 class="d-observe-title">${stick.title}</h3>
		                    <p class="d-observe-con">${stick.description}</p>
		                </a>
		            </div>
	            </#if>
            </#list>
        </div>
        
    	<div class="row d-observe-listWrap">
    		<#list praentChannelList as praent>
    			<#if praent.channelId==1>
    				<#list praent.list as son>
    					<#if son.channelId==7||son.channelId==8>
    						<#list son.contentList as content>
			                	<#if content.stickFlag=='0'>
			                		<#if son.channelId==7>
			                			<#if content_index<3>
							    		<div class="col-md-4">
							                <ul class="d-observe-list">
							                    	<li><a target="_blank" href="/xkcgc/${content.contentId}.html">${content.title}</a></li>
							                </ul>
							            </div>
						            	</#if>
						            </#if>
			                		<#if son.channelId==8>
							    		<div class="col-md-4">
							                <ul class="d-observe-list">
						                    	<li><a target="_blank" href="/xkcgc/${content.contentId}.html">${content.title}</a></li>
							                </ul>
							            </div>
						            </#if>
			                	</#if>
				            </#list>
			            </#if>
		            </#list>
	            </#if>
            </#list>
    	</div>
    </div>

    <#--Expert-->
    <div class="d-visualWrap" id="d_visual_anchor">
        <div class="container">
            <h2 class="d-title">
            <#list praentChannelList as praent>
    			<#if praent.channelId==2>
		        	<b>${praent.channelName }</b>
		        	<a target="_blank" href="/zjsj.html" class="d-more">MORE</a>
	        	</#if>
	        </#list>
            </h2>
            <div class="row">
				<#list stickExpertContent as content>
					<#if content_index<4>
		                <div class="col-md-3">
		                    <a target="_blank" href="/zjsj/${content.contentId}.html" class="d-observe-conWrap">
		                        <div class="d-visual-img">
		                        	<#if (content.picture)??>
		                       			<img src="${content.picture}" alt="">
		                       			<#else>
		                        			<img src="${ctx.basepath}/resources/front/img/visual%20-img.png" alt="">
		                        	</#if>
		                        </div>
		                        <p class="d-visual-name">${content.person }</p>
		                        <h3 class="d-visual-title">${content.title }</h3>
		                        <p class="d-visual-con">${content.description }</p>
		                    </a>
		                </div>
	                </#if>
                </#list>
            </div>
        </div>
    </div>

    <#--Server-->
    <#include "fuwufangan.ftl"/>

    <#--精品课程-->
    <div class="d-fineWrap" id="d_fine_anchor">
        <div class="container">
            <h2 class="d-title"><b>精品课程</b></h2>
            <h3 class="d-fine-title">通识课程 
            	<#list praentChannelList as praent>
	    			<#if praent.channelId==4>
			        	<a target="_blank" href="/jpkc.html" class="d-more">MORE</a>
		        	</#if>
		        </#list>
            </h3>
            <div class="row">
				<#list stickGeneralContent as content>
					<#if content_index<3>
		                <a class="col-md-4 d-fine-con" target="_blank" href="/jpkc/${content.contentId}.html">
		                    <div class="d-fine-imgWrap">
		                    	<#if (content.picture)??>
		                       		<img src="${content.picture }" alt="">
		                       		<#else>
		                       			<img src="${ctx.basepath}/resources/front/img/fine-img.png" alt="">
		                        </#if>
		                    </div>
		                     <p class="d-fine-conTitle">${content.title }</p>
		                </a>
		            </#if>
                </#list>
            </div>
            <h3 class="d-fine-title">学科课程 
            	<#list praentChannelList as praent>
	    			<#if praent.channelId==4>
			        	<a target="_blank" href="/jpkc.html" class="d-more">MORE</a>
		        	</#if>
		        </#list>
            </h3>
            <div class="row d-fine-conWrap">
				<#list subCourseContent as content>
					<#if content_index<3>
	                <a class="col-md-4 d-fine-con" target="_blank" href="/jpkc/${content.contentId}.html">
	                    <div class="d-fine-imgWrap">
	                        <#if (content.picture)??>
                       			<img src="${content.picture }" alt="">
	                       		<#else>
		                       		<img src="${ctx.basepath}/resources/front/img/fine-img.png" alt="">
	                        </#if>
	                    </div>
	                    <p class="d-fine-conTitle">${content.title }</p>
	                </a>
	                </#if>
                </#list>
            </div>
        </div>
    </div>

    <#--研修活动-->
    <div class="container d-training" id="d_training_anchor">
        <h2 class="d-title">
        	<#list praentChannelList as praent>
    			<#if praent.channelId==5>
		        	<b>${praent.channelName }</b>
		        	<a target="_blank" href="/yxhd.html" class="d-more">MORE</a>
	        	</#if>
	        </#list>
        </h2>
        <div class="row d-training">
			<#list stickActivityContent as content>
				<#if content_index<3>
		            <a class="col-md-4 d-fine-con" target="_blank" href="/yxhd/${content.contentId}.html">
		                <div class="d-fine-imgWrap">
		                    <#if (content.picture)??>
                        		<img src="${content.picture }" alt="">
	                        	<#else>
		                       		<img src="${ctx.basepath}/resources/front/img/fine-img.png" alt="">
	                        </#if>
		                    <#--遮罩层-->
		                    <div class="cornerTL"></div>
		                    <div class="cornerTR"></div>
		                    <div class="cornerBL"></div>
		                    <div class="cornerBR"></div>
		                </div>
		                <p class="d-fine-conTitle">${content.title }</p>
		            </a>
		         </#if>
	         </#list>
        </div>
    </div>

    <#--尾部-->
    <#include "footer.ftl"/>
    <#--版权信息-->
	<#include "copy.ftl"/>
    <#--右侧悬浮按钮-->
    <div id="top"></div>

    <script>
        <#--Server tab-->
        $('.d-service-btnBg').mouseover(function(){
            $('.d-service-tab>div').eq($(this).index()/2).addClass('active').siblings().removeClass('active');
            $(this).find('.d-btn-icon').addClass('active').parent().siblings().find('.d-btn-icon').removeClass('active');
            $(this).addClass('active').siblings().removeClass('active');
            $(this).next().removeClass('active').siblings('.d-service-btnBg-replace').addClass('active');
        })

        <#--轮播图-->
        var mySwiper = new Swiper ('.swiper-container', {
            direction: 'horizontal',
            loop: true,
        	autoplay: true,

            <#--如果需要分页器-->
            pagination: {
                el: '.swiper-pagination',
                clickable :true,
            },

            <#-- 如果需要前进后退按钮-->
            navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
            },

            <#--回调函数,文字描述动态效果-->
            on: {
                slideChangeTransitionEnd: function(){
                    $('.d-slide-text').eq(this.activeIndex).addClass('d-slide-text-active').parent().siblings().find('.d-slide-text').removeClass('d-slide-text-active');
                },
            },

        })
        

        <#--轮播图鼠标移入箭头效果-->
        $('.swiper-container').mouseover(function(){
        	mySwiper.autoplay.stop();
            $('.swiper-button-prev').css('display', 'block');
            $('.swiper-button-next').css('display', 'block');

        }).mouseout(function(){
        	mySwiper.autoplay.start();
            $('.swiper-button-prev').css('display', 'none');
            $('.swiper-button-next').css('display', 'none');
        })

        <#--内容信息导航锚点-->
        $('.d-nav-new-Wrap').navScroll({
            mobileDropdown: true,
            mobileBreakpoint: 768,
            scrollSpy: true
        });

        $('.hero__scroll').on('click', function(e) {
            $('html, body').animate({
                scrollTop: $(window).height()
            }, 1200);
        });
        
        <#--导航跳转-->
        $('.d-logo-text img').click(function(){
        	window.location.href='/'; 
        })
        $('.d-about-us-nav').click(function(){
        	window.location.href='/about/aboutUs.html'; 
        })
        
        
    </script>
	<script>
	//自动推送代码
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
	
	
	</script>

<!--     百度统计和cnzz统计 -->
<script src="${ctx.basepath}/resources/front/js/baidu-count.js"></script>
<script src="${ctx.basepath}/resources/front/js/cnzz-count.js"></script>

</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

01096110

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值