内容摘要:
Jive作为学习设计模式的好教材,难度自然不低,我们就以Jive(J道版)为例程,带着大家来一次“面向过程式”的逐步阅读源代码,好像一次游览一般。本次浏览forum.jsp。
作者:蔡永航
(注:James Shen的学弟) 南京工业大学工商管理三年级学生
|
一.forum.jsp简介
Forum.jsp的任务为显示位于指定论坛中的主题,并将论坛中的主题的标题呈现给访问者,同时对讨论主题的显示进行分页处理。我们最通常地调用forum.js有两种途径。途经一,如图1所示,点击index.jsp页面上的论坛名称,此时的URL为:http://localhost:8083/forum/forum.jsp?forum=1(根据论坛的不同,这里forum后面的数字将会不同);途径二,如图2所示,点击forum.jsp左上角和左下角的分页标签,此时的URL为:http://localhost:8083/forum/forum.jsp?forum=1&start=15&thRange=15
(根据论坛和当前所处页数的不同,url后面的参数值将会有所变化)。
图1
图2
二.forum.jsp的六大部分
为了便于弄清forum.jsp到底做了哪些事情,我们在这里将forum.jsp的功能分解为6个部分,如下图所示:
三.forum.jsp代码分析
1.取得当前板块信息
这一部分较为简单,代码如下所示:
<%
// Get parameters
long forumID
= ParamUtils.getLongParameter(request,"forum",-1L);
int start
= ParamUtils.getIntParameter(request,"start",0);
int range
= myEnv.du.getThreadRange(request,response,pageUser);
// Load the forum
Forum forum
= myEnv.getForumFactory().getForum(forumID);
%>
ParamUtils类中的静态方法getXXXParameter(request,
"XXX",-1L
)表示从request中取得参数为“XXX”的值,并将其返回,如若该值不存在,那么便将返回默认值类型为Long的值-1。拿http://localhost:8083/forum/forum.jsp?forum=1
举例,此时forumID值为1,而strat不存在,所以返回默认值0,此时start值为0。myEnv.du.getThreadRange(request,response,pageUser)为取得的每页显示的主题数,这在上一篇情景分析index.jsp中有讲述,大体流程为程序首先判断request中是否有参数thRange,如果thRange存在,并且值大于-1,那么当前页面显示的主题数即为参数thRange的值;如果不存在参数thRange,那么程序将取得默认的显示数,并且判断当前用户是否为注册用户,倘若为注册用户,那么将当前的每页显示主题数由用户设置的值决定,若为匿名浏览用户,则取得匿名用户在访问index.jsp中程序为客户端存储的每页显示主题数。
该部分的重点为myEnv.getForumFactory().getForum(forumID)。通过myEnv.getForumFactory()得到经过代理模式包装过的DBForumFactory。在用户通过ForumFactoryProxy调用DBForumFactory的getForum(forumID)得到论坛实例后将进行相应的权限处理,并将相应的权限和得到的论坛实例包装成ForumProxy类的实例返回给客户端,当客户端对论坛执行某项操作时将进行权限检查,以判断用户是否有执行某项操作的权利。
DBForumFactory.getForum(forumID)将首先从缓存中查找时候已经有该论坛的实例,如果没有册从数据库读取,并将DBForum中的属性值用位于数据库中存储的属性进行赋值操作,在赋值操作结束后会建立该论坛的内容过滤器(此部分将在另外一篇文章中讨论,注意这里的内容过滤器与上文图中的构建主题的显示过滤器是两个不同概念)。
2.构建主题显示过滤器
主题显示过滤器实际上为一个结果集过滤器,只有符合结果集过滤器要求的结果才能够返回到forum.jsp页面以供调用。
<%
// Create a ResultFilter for creating
thread iterators and getting thread
// counts
ResultFilter filter
= new
ResultFilter();
filter.setStartIndex(start);
filter.setNumResults(range);
// Set the moderation level minimum
filter.setModerationRangeMin(forum.getModerationMinThreadValue());
// More forum properties
int numThreads
= forum.getThreadCount(filter);
int numMessages
= forum.getMessageCount(filter);
%>
这里首先建立一个结果集过滤器对象实例filter,接着将取得的start和rang的值以及forum.getModerationMinThreadValue()得到的值通过set方法赋予filter中的相关字段。如start
= 0 ,rang =15 ,forum.getModerationMinThreadValue()
= 1
则表示该结果集将取得第1-15条主题,并且这些的主题中的modValue值大于等于1。
接着将通过DBForum中的方法getThreadCount(filter)和getMessageCount(filter)获得该论坛当前的主题数和当前的帖子总数。下面让我们潜入getThreadCount(filter)方法中。
getThreadCount(filter)代码:
public int
getThreadCount(ResultFilter resultFilter)
{
String query
= getThreadListSQL(resultFilter,
true);
(1)
CacheableInt count
= (CacheableInt)
threadCountCache.get(query);
(2)
// If already in cache, return the
count.
if (count
!= null)
{
return count.getInt();
}
// Otherwise, we have to load the count
from the db.
else {
int threadCount
= 0;
Connection con
= null;
Statement stmt
= null;
try {
con =
ConnectionManager.getConnection();
stmt =
con.createStatement();
ResultSet rs
= stmt.executeQuery(query);
rs.next();
threadCount =
rs.getInt(1);
(3)
} catch
(SQLException sqle)
{
sqle.printStackTrace();
} finally
{
try {
stmt.close();
} catch
(Exception e)
{
e.printStackTrace();
}
try {
con.close();
} catch
(Exception e)
{
e.printStackTrace();
}
}
// Add the thread count to cache
threadCountCache.add(query, new
CacheableInt(threadCount));
return threadCount;
}
}
(1)首先将resultFilter传入getThreadListSQL()以构建查询当前主题总数的SQL语句,getThreadListSQL()将根据resultFilter中的值来产生相应的SQL语句第二个参数为ture表示该SQL语句为统计总数的SQL语句,而非查询SQL语句。构建完成的SQL语句如下所示:
SELECT
count(1) FROM jiveThread WHERE jiveThread.forumID=1 AND
jiveThread.modValue >= 1
(2)将该产生的SQL语句作为散列表主键对从缓存中查询该SQL语句的统计出来的结果。如果得到的结果不为空则直接返回,如果为空就老老实实地从数据库中统计。
(3)得到统计结果。
getMessageCount(filter)的实现大同小异,下面只给出构建出的SQL语句作为参考:
SELECT
count(1) FROM jiveMessage WHERE forumID=1 AND modValue >= 1
3.取得主题列表
这便是forum.jsp的关键所在了,代码如下所示:
<%
// Iterator
of
threads
ForumThreadIterator
threads
= forum.threads(filter);
%>
程序将filter传入forum.threads(),以取得符合结果集过滤器的主题的ID。现在让我们潜入forum.threads(filter),代码如下所示:
threads(ResultFilter
resultFilter)代码:
public ForumThreadIterator
threads(ResultFilter resultFilter)
{
String query
= getThreadListSQL(resultFilter,
false);
(1)
long[]
threadBlock =
getThreadBlock(query.toString(),
resultFilter.getStartIndex());
(2)
int startIndex
= resultFilter.getStartIndex();
int endIndex;
// If number of results is set to
inifinite, set endIndex to the total
// number of threads in the forum.
if (resultFilter.getNumResults()
== ResultFilter.NULL_INT)
{
(3)
endIndex =
(int)
getThreadCount(resultFilter);
}
else {
endIndex =
resultFilter.getNumResults() +
startIndex;
}
return new
ForumThreadBlockIterator(threadBlock,
query.toString(),
startIndex, endIndex,
this.id,
factory);
(4)
}
(1)通过给定的resultFilter构建SQL语句,构建完成的SQL语句如下所示:
SELECT
jiveThread.threadID, jiveThread.modifiedDate FROM jiveThread WHERE
jiveThread.forumID=1 AND jiveThread.modValue >= 1 ORDER BY
jiveThread.modifiedDate DESC
(2)得到符合查询条件的Block,关于Block是一个默认情况下拥有200长度的ID号的集合,当每个页面显示的主题数量小于单个Block的长度的时候,getThreadBlock()返回的实际上是不但包含了需要显示页面的主题ID还包括下几个页面的主题ID,并且程序将每个block通过query
+ blockID
作为主键进行缓存,等到用户进行下一页浏览,并且主题ID的范围没有超过当前的Block的时候就不必从数据库中查询符合查询条件的主题ID号了,直接从Block中即可取得,这样大大提高了程序的效率。Block的概念图如下:
(3)这里确定当前页面返回的主题数的开始和末尾ID,如果没有设置单个页面显示的主题数,那么程序将返回该版块所有主题。
(4)程序最后返回一个ForumThreadBlockIterator,以供页面进行迭代。
4.显示当前页面主题
在数据库JiveThread表中并没有保存主题的任何文字信息,只指明了包含主题内容的帖子的ID,即rootMessageID字段的值,这个值与表JiveMessage中的MessageID相对应,即主题的文字信息保存在JiveMessag表中。所以想要在页面上显示主题的内容,就必须得到相应的Message内容。代码如下:
ForumMessage
rootMessage = thread.getRootMessage();
让我们潜入DbForumThread看看getRootMessage()中作了什么
getRootMessage()代码:
public ForumMessage
getRootMessage() {
ForumMessage rootMessage
= null;
try {
rootMessage =
getMessage(rootMessageID);
}
catch (Exception
e) {
e.printStackTrace();
}
return rootMessage;
}
此处的rootMessageID在初始化DbForumThread就已经赋值了。
getMessage()代码:
public ForumMessage
getMessage(long
messageID) throws
ForumMessageNotFoundException {
return factory.getMessage(messageID,
this.id,
forumID);
}
实际上程序还是借由DBForumFactory中读取贴子信息,还是那句话,缓存中有就直接读取,没有就老老实实从数据中读取,除了和缓存打交道外,还要对贴子进行信息过滤,这又是另外一个话题了。
5.分页处理
让我们分段来查看这些代码,
<%
// Number of
int numPages
= 0;
if (numThreads
!= range)
{
numPages =
(int)Math.ceil((double)numThreads/(double)range);
} else
{
numPages =
1;
}
%>
这断代码判断当前版块中的主题按照每页显示的页数可以分成多少页。
<%
String
hrefname="forum.jsp?forum="+forumID;
String
paginatorHTML = myEnv.du.getForumPaginator(hrefname, numThreads,
numPages, start, range);
%>
myEnv.du.getForumPaginator()调用的即为DisplayUtil中的静态方法getForumPaginator()。这个方法负责生成分页的HTML代码,范例如下:
X
page(s) [ 1 2 3 4 5 ... 30 | > ]
|