锁定老帖子 主题:FreeMarker三宗罪!
精华帖 (2) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-08
一宗罪:freemarker的变量必须有值,没有被赋值的变量就会抛出异常,那个黄黄的freemarker出错页面,真是让人看了太难过了。 freemarker的FAQ上面冠冕堂皇的说,未赋值的变量强制抛错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。但是实际的效果是:带来的是非常大的编程麻烦,程序里面几乎所有可能出现空值的变量统统需要加上${xxx?if_exists},有些循环条件还需要写if判断,这样不但没有杜绝应该杜绝的错误,反而极大增加了编程的麻烦。 二宗罪:freemarker的map限定key必须是string,其他数据类型竟然无法操作!这一点就不讲了,JavaEye上面已经有人抱怨过了。连Webwork的开发人员Pat Lightboy都在抱怨这一点。 三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放! freemarker支持在页面里面直接操作Session,request等,例如${Session[...]},方便确实很方便,但是一旦需要做群集,就会报错。 今天是b051问起我这个问题,他在做Tomcat群集的时候发现freemarker报错,HttpSessionHashModel不可序列化。他修改该类源代码,让他实现序列化接口,仍然报错。我一看,HttpSessionHashModel包含的属性: private HttpSession session; private final ObjectWrapper wrapper; // These are required for lazy initializing session private final FreemarkerServlet servlet; private final HttpServletRequest request; private final HttpServletResponse response; 登时晕倒,这样的东西还往Session里面放?bad smell! 严重警告应用需要往群集上面发布应用的同学们,千万别用freemarker! 不过瑕不掩瑜,freemarker也是有优点的: 1、易学易用 我是看了一天文档就用得挺熟练了,freemarker文档写得太好了,例子丰富,照做一遍全都会了。 2、功能强大 比Velocity强大多了,还支持JSP Tag。不过最有意义的是macro功能,可以自定义常用的macro,实现页面常规操作的可复用性。 3、报错信息友好 很多应用服务器的JSP报错信息是无法定位到源代码行的。不过freemarker报错定位很准确,丝毫不差,而且信息丰富,一看就知道怎么回事(虽然那个黄黄的页面看起来让人难受) 总之,用与不用,还是看大家衡量了。我是不想再用freemarker了,准备回归JSP Tag了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-12-09
robbin 写道 三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放! freemarker支持在页面里面直接操作Session,request等,例如${Session[...]},方便确实很方便,但是一旦需要做群集,就会报错。 今天是b051问起我这个问题,他在做Tomcat群集的时候发现freemarker报错,HttpSessionHashModel不可序列化。他修改该类源代码,让他实现序列化接口,仍然报错。我一看,HttpSessionHashModel包含的属性: private HttpSession session; private final ObjectWrapper wrapper; // These are required for lazy initializing session private final FreemarkerServlet servlet; private final HttpServletRequest request; private final HttpServletResponse response; 登时晕倒,这样的东西还往Session里面放?bad smell! 严重警告应用需要往群集上面发布应用的同学们,千万别用freemarker! 前面两个问题我有同感,但是还能接受... 对于第三个问题,不同意Robbin具有强烈煽动性的:千万别用freemarker! 关键在怎么用~~ 只有在使用FreemarkerServlet的时候, HttpSessionHashModel才会被创建和使用..具体可参见 FreemarkerServlet.java 的 createModel() 方法... 我从来不用这个FreemarkerServlet,也就不会出现Robbin所说的第三个问题了.. 我们在开发中用的是Springmvc, FreeMarker只在 springmvc 要 rend view的时候被调用(我们重写了FreeMarkerView), 而不是通过FreemarkerServlet: Environment env = template.createProcessingEnvironment(model, writer);; freemarkerConfigurer.includeTemplatesToEnv(env);; env.process();; 其中的model,writer完全在我们自己的控制之中,不可序列化的东西完全可以剔除. 并且, Springmvc 在 expose Session to view 的时候,是这样实现的: if (this.exposeSessionAttributes); { HttpSession session = request.getSession(false);; if (session != null); { for (Enumeration en = session.getAttributeNames();; en.hasMoreElements();;); { String attribute = (String); en.nextElement();; if (model.containsKey(attribute); && !this.allowSessionOverride); { throw new ServletException("Cannot expose session attribute '" + attribute + "' because of an existing model object of the same name");; } Object attributeValue = session.getAttribute(attribute);; if (logger.isDebugEnabled();); { logger.debug("Exposing session attribute '" + attribute + "' with value [" + attributeValue + "] to model");; } model.put(attribute, attributeValue);; } } } 干净利索! 在FreeMarker rend view 的时候, 它的 Model 中没有一点request,response,session的影子了.. 在FreeMarker模版中,要拿到session中的值,只需要${key}就可以了,不必使用${Session[...]}. |
|
返回顶楼 | |
发表时间:2005-12-09
robbin 写道 一宗罪:freemarker的变量必须有值,没有被赋值的变量就会抛出异常,那个黄黄的freemarker出错页面,真是让人看了太难过了。 freemarker的FAQ上面冠冕堂皇的说,未赋值的变量强制抛错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。但是实际的效果是:带来的是非常大的编程麻烦,程序里面几乎所有可能出现空值的变量统统需要加上${xxx?if_exists},有些循环条件还需要写if判断,这样不但没有杜绝应该杜绝的错误,反而极大增加了编程的麻烦。 偶倒认为这个是极好的特性,而且很赞同它对于null的处理,原因在FreeMarker的FAQ上已经解释得很清楚了。 在偶们的项目里,是应用不同的Error Handler在开发环境/测试环境/生产环境来解决的。 具体的可以参考: http://freemarker.sourceforge.net/docs/pgui_config_errorhandling.html 在生产环境下,可以用它自带的TemplateExceptionHandler.IGNORE_HANDLER 再加上好用的if_exists, default这些exists built-in,处理各种null的情况真的是很方便: ${(bar.foo.value)?default("N/A")} 当然,对所有可能为空的值加上built-in确实是一件麻烦的事情,但是偶们所有的页面都是用form macro做的,所以就没有你的烦恼了,:) robbin 写道 二宗罪:freemarker的map限定key必须是string,其他数据类型竟然无法操作!这一点就不讲了,JavaEye上面已经有人抱怨过了。连Webwork的开发人员Pat Lightboy都在抱怨这一点。 这个绝对是很烦恼的一件事情,其实这个问题是TemplateModel造成的,偶最不能理解的是为啥它需要弄一个TemplateModel来wrap所有的Object,直接用原始对象不就好了么,还要弄个wrap/unwrap。据说它这样的做法是提高性能,但是偶无法理解......,有人可以解释一下么? robbin 写道 三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放! 不知道你在说啥,freemarker只是template language,和HTTP session有啥关系啊? freemarker/velocity这些模本语言和JSP Tag比较起来,最大的优点是性能可以做得很好 而freemarker和velocity比较起来,最大的优点是它在活跃开发,可以不断地进步,webwork选择freemarker作为默认的template,很大原因在于此。 |
|
返回顶楼 | |
发表时间:2005-12-09
原来一直是用JSTL的,最近刚刚开始使用Freemarker。
对于robbin所说的第一重罪,我深有体会啊!${xxx?if_exists}这东西让我太不爽了,JSTL对于null值就是处理为空字符串的,所以适应这东西花了我很长时间。 第二重罪我体会不深,不过必须用String来做Key的似乎的确很让人头痛。 第三重,我用webwork和Freemarker的整合方式,没仔细研究里面的东东。 |
|
返回顶楼 | |
发表时间:2005-12-09
我一直在用freemarker,特别是它的macro真是太强大了,已大大减少了我的页面开发和维护的工作量
|
|
返回顶楼 | |
发表时间:2005-12-13
我也是在改既有的项目的时候发现几分问题向robbin请教的。
的确,从2.1.X,WW就不建议写freemarkerServlet了,可是去掉他又是如何一个大工程! 为什么是大工程,要怪,我想肯定的怪freemarker拥有一个不可以拥有的特性,就是和session的互操作。造成我们这里很多开发人员随意把它当作jsp来用。 如果说到一个框架还需要人们去去粗取精的话,那他怎么会被广泛使用? |
|
返回顶楼 | |
发表时间:2005-12-14
Freemarker 最大的好处是对 xml 的转换,比 xslt 好用多了。
|
|
返回顶楼 | |
发表时间:2005-12-14
个人认为FreeMarker是当前最好的用来渲染view的模板叻,希望robbin不要浅尝辄止啊,这么好的东西,轻易丢弃就太可惜叻。就好像小时候我们学骑自行车一样,你说好好的自行车四个轮子多好,为啥只有两个轮子,那么容易就摔倒呢。可是学会之后,这辈子都会骑,都觉得两个轮子的自行车的确比四个轮子的好。
一宗罪,null值处理: 这个也的确让我烦叻一阵子,不过后来不仅习惯叻,而且还真的喜欢上这一点叻。我们的页面上有null值,大概有这样两种情况: 1、这个值本来就是可有可无的。比如表单域的value=""值。这种情况,可以用${foo?default("")}来写,并不十分复杂,而且可以让你狠方便地定义默认值,比如N/A什么的。 2、Action层处理有错,导致null值。这种情况下,FreeMarker就给程序调试提供的有力的保障,也是FreeMarker对null值如此敏感的初衷。从我学FreeMarker开始,我就强烈地感受到FreeMarker在试图把自己做成模板语言中的强类型语言。 总之,这个null其实不是什么大问题啦,并没有什么不方便。 二宗罪:Map的key必须是String 这个我还真的没遇到过。我写叻段小程序简单地试验叻下,用list遍历map.keySet(),然后用map.get(key)这样的方法查询,是可以的。直接用map[]这样的操作的确是不行。我实际使用中在前台用到Map的情况不多,以前用velocity的时候也没觉得map有多好用,大部分情况我是用n个相同长度的list或数组解决的,感觉比map方便。 三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放! 这个就不说啦,跟freemarker无关。抛开这个不谈,客观地说,WebWork对FreeMarker Result的封装还是非常不错的。 FreeMarker自身的优点的确非常突出,易学,我只用叻3个小时看叻遍文档就基本掌握叻,就可以扔掉velocity叻,哈哈。而且FreeMarker自己的builtin也的确有趣,虽然大部分我都不用,还是要在action层自己封装个方法调用,不过常用的比如?html,?date这样的还是狠方便的。 我想说说FreeMarker跟JSP Tag的比较,前面看到Robbin说要回归JSP Tag,我真是痛心疾首啊!要拿FreeMarker去攻击JSP Tag,我都不需要列举JSP Tag/JSTL的缺点,只要把FreeMarker的Macro拿出来往那一亮就OK叻。用老罗的话说,“那简直是太方便叻!太方便叻!!” 我刚才就在重构一个项目的common.ftl,将公共的部分提取出来,细力度的重构。在Code级别我们可以重构,可以代码only once。现在在FreeMarker的帮助下,页面级也可以这样叻!JSP Tag、Velocity不是不能这样,而是都太麻烦,而且功能不强,用叻不仅不省工作量,反而还更烦。而只有FreeMarker,才能让页面达到这样的重构高度。 我们这边的策划总是有新点子,页面也总是改,我写代码是基于敏捷原则,现在页面也是敏捷的,可以说是真正做到的拥抱变化,感觉非常爽。 |
|
返回顶楼 | |
发表时间:2005-12-22
关于第三宗罪,我今天仔细看了一下代码,确有其事。
在Freemarker自带的FreemarkerServlet的createModel方法中有这样一段代码: // Create hash model wrapper for session HttpSessionHashModel sessionModel; HttpSession session = request.getSession(false); if(session != null) { sessionModel = (HttpSessionHashModel) session.getAttribute(ATTR_SESSION_MODEL); if (sessionModel == null) { sessionModel = new HttpSessionHashModel(session, wrapper); session.setAttribute(ATTR_SESSION_MODEL, sessionModel); initializeSession(request, response); } } else { sessionModel = new HttpSessionHashModel(this, request, response, wrapper); } params.putUnlistedModel(KEY_SESSION, sessionModel); 会每次把sessionModel放进HttpSession,不知道究竟什么用意。这里有一个问题是:当我们使用FreeMarker作为sitemesh的decorator的时候,必须使用FreeMarkerServlet,看sitemesh的扩展类: public class FreemarkerDecoratorServlet extends FreemarkerServlet 然后再看看webwork使用FreeMarker作为view的时候, FreemarkerResult在createModel里面引用了FreemarkerManager.getInstance().buildTemplateModel,而FreemarkerManager的buildScopesHashModel里面: TemplateHashModel sessionModel; HttpSession session = request.getSession(false); if (session != null) { model.put(KEY_SESSION_MODEL, new HttpSessionHashModel(session, wrapper)); } else { // no session means no attributes ??? // model.put(KEY_SESSION_MODEL, new SimpleHash()); } 这里每次需要sessionModel,就是直接new一个了事,并没有每次放进HttpSession下次再从HttpSession中取得。 因此使用freemarker作为webwork的view,是没有问题的。但是如果使用freemakrer作为sitemesh的decorator的时候,还是有这个群集复制的问题。我想修改FreeMarkerServlet,改成这样: HttpSessionHashModel sessionModel; HttpSession session = request.getSession(false); if(session != null) { sessionModel = new HttpSessionHashModel(session, request, response, wrapper); }else { sessionModel = new HttpSessionHashModel(this, request, response, wrapper); } params.putUnlistedModel(KEY_SESSION, sessionModel); 应该就行了,不过我还没有测试过。 |
|
返回顶楼 | |
发表时间:2005-12-22
robbin 写道 这里有一个问题是:当我们使用FreeMarker作为sitemesh的decorator的时候,必须使用FreeMarkerServlet. 这句话偶不同意...看看: http://forum.iteye.com/viewtopic.php?t=15632 这是以前偶的一个帖子,里面的代码有一点问题..但是现在都已经很完美的解决了.. |
|
返回顶楼 | |