先说说本人的技术背景,以免说错了被砖砸!在确定用roller做二次开发之前,本人没有用struts2、velocity、openjpa,只是匆匆看了一本《深入浅出struts2》电子书,然后是一边读roller源代码一边摸索这些技术是怎么实现相关功能的。到今天已经半个月有余,写博当备忘。
在今天的这篇文章中我打算谈谈roller是如何展现一个博客页面的,我们知道一个页面最终在用户的浏览器被展现是一些html代码。生成html代码有很多种方式和技术,roller使用的是velocity模板技术。个人觉得roller使用velocity可能有以下原因:
1、roller要考虑博客页面展现的多样性
2、博客页面主题的可自定义
velocity的模板技术无疑很好的符合这两点要求。
在roller中,是怎样用velocity来展现页面的呢?先来说说roller中主题、样式、模板的关系,如果没有好好使用一下roller的自定义主题的功能而是仅仅看源代码,还真有点难于搞清楚roller是如何展现页面的。
在roller中,一个主题对应一个样式,一个主题可以包含多个模板,模板是页面上的一些部件,比如菜单栏可以用一个模板;日历也可以用一个模板等等。当用户选择不同的主题时,由于主题包含的模板不同则展现的内容也不同。样式就是我们一般指的css代码。
下面我们按照程序的执行顺序来说说roller页面的生成过程。在前面的文章《说说ROLLER的PageServlet类》中我们已经谈了一些相关的内容,比如用户想直接进入某个博客abc,当缓存中没有时就要生成页面。我们今天就接着这里开始讲。
roller是一个多用户博客系统,每个博客要展现的内容都不一样。当用户要进入abc时,roller用一个Weblog类的对象来代表abc,通过这个对象就可以获取到这个博客相关的一些内容,比如博客名称、博客日志列表、博客使用的主题等等。
有了Weblog对象,我们也就可以知道某个博客使用的是什么主题,此主题包含多少个模板了。有了主题roller仅仅知道要展现成什么样子,而里面的数据从哪里来?
roller把博客页面内容各部抽象成一个Model接口,比较代表菜单的、有代表页面的等等,此接口有两个方法:
1、public String getModelName();
2、public void init(Map params) throws WebloggerException;
此接口有几个实现类,分别是:CalendarModel、ConfigModel、MenuModel、PageModel、SiteModel等。这些类的对象是要在velocity的模板文件中被使用的。引用的名字是他们的getModelName方法返回的内容,比如下面vm代码:
################ weblog.vm ####################### #set($pager = $model.getWeblogEntriesPager()) <div class="next-previous"> #if ($model.results) #showWeblogSearchAgainForm($model.weblog) #showNextPrevSearchControl($pager) #else #showNextPrevEntriesControl($pager) #end </div> ################## menu-list.vm ##################### <div id="menu"> #foreach( $menu in $menuModel.getMenus() )
分别引用的对象是PageModel、MenuModel。
在PageServlet中,执行下面代码完成一个页面的数据组织:
// Load models for pages String pageModels = WebloggerConfig.getProperty("rendering.pageModels"); ModelLoader.loadModels(pageModels, model, initData, true);
完成数据的组织就该交的velocity来生成页面了。在PageServlet中,roller先根据模板对象去获取一个Renderer,然后创建一个CachedContent缓存对象,先把Renderer生成的页面内容放在CachedContent中,再通过流把页面输出到用户的浏览器中。
如何获得一个渲染器renderer,roller费了一番周折。
renderer = RendererManager.getRenderer(page);
在这个getRenderer()方法中,RendererManager一开始就被初始化,并且从roller.properties中读取用户配置的rendering.rollerRendererFactories值,如果用户没有特殊要求的话,一般是这个值:
org.apache.roller.weblogger.ui.rendering.velocity.VelocityRendererFactory
RendererManager会把请求交给VelocityRendererFactory这个类的对象处理。VelocityRendererFactory会new一个VelocityRenderer对象返回,但VelocityRenderer对象是roller自己定义的类,真正干活的是velocity的Template类,此类作为VelocityRenderer的一个属性,当new一个VelocityRenderer时,调用velocityEngine.getTemplate(name, encoding)方法一个velocity的Template 。
剩下的事情就交给velocity引擎去做了,velocity怎么去构建一个他它自己的Template对象我就不清楚了。但通过观察velocity.properties文件发现,这两个类值得留意:
ThemeResourceLoader、RollerResourceLoader。
这两个类都继承了velocity的ResourceLoader类,其中的都有public InputStream getResourceStream(String name)方法,这个方法跟加载模板内容有关!
ThemeResourceLoader会调用ThemeManager的方法,而ThemeManager在启动的时候会把系统预定义的模板加载到内存中。
RollerResourceLoader会调用数据库去获取一个模板内容。
这里得说说roller为了主题的多样化、个性化而提供的一个自定义主题功能,如果用户自定义了主题,则roller会把主题相关的模板等信息保存到db中,因此就需要到db中查询主题模板信息。
从velocity引擎得到template后,渲染器Renderer在PageServlet中被用来生产页面,具体就是由velocity的template来做,怎么做?笔者精力有限先了解到这。
题外话:roller有个功能,就是可以把某个博客设置为站点主页。如果我们用roller来做一个小网站,又不想搞得太复杂,可以专门做一个主页的模板,并且有一个博客使用,再用一个类取主页相关的数据并实现Model,这样就可以用既有缓存功能又简单。所以好的代码扩展就是比较方便!
下次说说roller如何缓存页面。