JSP入门教程(DOC格式)-第12部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
ControllerServlet
/user/*
ControllerServlet
/admin/*
ControllerServlet
*。do
java 的复杂性在此处显露无疑。实际使用时,最好不要依赖web。xml 中的配置,
在自己的类中实现灵活配置才是正途。
7。3。2。 过滤链
其实在 07…02 这个例子里,我们使用了两个过滤器,EncodingFilter 负责设置
编码,SecurityFilter 负责控制权限,那这两个过滤器是怎么起作用的呢?它
们两个同时过滤一个请求时谁先谁后呢?
下面这个图会告诉我们答案。
所有的奥秘就在 Filter 中的FilterChain 中。服务器会按照web。xml 中过滤器
定义的先后循序组装成一条链,然后一次执行其中的 doFilter()方法。执行的
顺序就如上图所示,执行第一个过滤器的 chain。doFilter()之前的代码,第二
个过滤器的 chain。doFilter()之前的代码,请求的资源,第二个过滤器的
75 / 148
…………………………………………………………Page 76……………………………………………………………
chain。doFilter()之后的代码,第一个过滤器的 chain。doFilter()之后的代码,
最后返回响应。
因此在 07…02 中执行的代码顺序是:
1。 执行 EncodingFilter。doFilter()中 chain。doFilter()之前的部分:
request。setCharacterEncoding(〃gb2312〃);
2。 执行 SecurityFilter。doFilter()中 chain。doFilter()之前的部分:判断
用户是否已登录。
如果用户已登录,则访问请求的资源:/admin/index。jsp。
如果用户未登录,则页面重定向到:/failure。jsp。
3。 执行 SecurityFilter。doFilter()中 chain。doFilter()之后的部分:这里
没有代码。
4。 执行 EncodingFilter。doFilter()中 chain。doFilter()之后的部分:这里
也没有代码。
过滤链的好处是,执行过程中任何时候都可以打断,只要不执行
chain。doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时,
就要特别注意过滤链的执行顺序问题,像 EncodingFilter 就一定要放在所有
Filter 之前,这样才能确保在使用请求中的数据前设置正确的编码。
7。4。 filter 的详细配置
我们已经了解了 filter 的基本用法,还有一些细节配置在特殊情况下起作用。
在 servlet…2。3 中,Filter 会过滤一切请求,包括服务器内部使用 forward 转
发请求和的情况。
到了 servlet…2。4 中Filter 默认下只拦截外部提交的请求,forward 和 include
这些内部转发都不会被过滤,但是有时候我们需要 forward 的时候也用到
Filter,这样就需要如下配置。
TestFilter
anni。TestFilter
TestFilter
/*
REQUEST
FORWARD
76 / 148
…………………………………………………………Page 77……………………………………………………………
INCLUDE
EXCEPTION
这样 TestFilter 就会过滤所有状态下的请求。如果我们没有进行设置,默认使
用的就是 REQUEST。而EXCEPTION 是在 isErrorPage=〃true〃的情况下出现的,这
个用处不多,看一下即可。
这里 FORWARD 是解决request。getDispatcher(〃index。jsp〃)。forward(request;
response);无法触发 Filter 的关键,配置上这个以后再进行 forward 的时候就
可以触发过滤器了。
Filter 还有一个有趣的用法,在 filter…mapping 中我们可以直接指定
servlet…mapping,让过滤器只处理一个定义在web。xml 中的 servlet。
TestFilter
TestServlet
TestServlet
anni。TestServlet
TestServlet
/TestServlet
直接指定 servlet…name,TestFilter 便会引用 TestServlet 配置的
url…pattern,在某些 filter 与 servlet 绑定的情况下不失为一个好办法。
77 / 148
…………………………………………………………Page 78……………………………………………………………
第 8 章 配置 listener 监听器
注意
还记得我们之前讲过的在线列表吗?第 4。2 节 “例子:在线列表”。我们曾经
说过那个在线列表无法判断用户非法退出,很可能造成在线列表无限增大,现在
我们可以用 listener 来弥补这一问题了。
如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:
第 9 章 封装 taglib 组件。
1。 了解如何使用HttpSessionListener 监听 session 的销毁。
2。 了解如何使用 HttpSessionBindingListener 监听 session 的销毁。
8。1。 使用 HttpSessionListener
编写一个 OnlineUserListener。
package anni;
import java。util。List;
import javax。servlet。ServletContext;
import javax。servlet。http。HttpSession;
import javax。servlet。http。HttpSessionListener;
import javax。servlet。http。HttpSessionEvent;
public class OnlineUserListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event。getSession();
ServletContext application = session。getServletContext();
// 取得登录的用户名
String username = (String) session。getAttribute(〃username〃);
// 从在线列表中删除用户名
List onlineUserList = (List)
application。getAttribute(〃onlineUserList〃);
onlineUserList。remove(username);
78 / 148
…………………………………………………………Page 79……………………………………………………………
System。out。println(username + 〃超时退出。〃);
}
}
OnlineUserListener 实现了 HttpSessionListener 定义的两个方法:
sessionCreated()和 sessionDestroyed()。这两个方法可以监听到当前应用中
session 的创建和销毁情况。我们这里只用到 sessionDestroyed()在 session
销毁时进行操作就可以。
从 HttpSessionEvent 中获得即将销毁的 session,得到 session 中的用户名,
并从在线列表中删除。最后一句向 console 打印一条信息,提示操作成功,这只
是为了调试用,正常运行时删除即可。
为了让监听器发挥作用,我们将它添加到 web。xml 中:
anni。OnlineUserListener
以下两种情况下就会发生 sessionDestoryed (会话销毁)事件:
1。 执行 session。invalidate()方法时。
既然 LogoutServlet。java 中执行 session。invalidate()时,会触发
sessionDestory()从在线用户列表中清除当前用户,我们就不必在
LogoutServlet。java 中对在线列表进行操作了,所以
LogoutServlet。java 的内容现在是这样。
public void doGet(HttpServletRequest request;HttpServletResponse
response)
throws ServletException; IOException {
// 销毁 session
request。getSession()。invalidate();
// 成功
response。sendRedirect(〃index。jsp〃);
}
2。 如果用户长时间没有访问服务器,超过了会话最大超时时间,服务器就会
自动销毁超时的session。
79 / 148
…………………………………………………………Page 80……………………………………………………………
会话超时时间可以在 web。xml 中进行设置,为了容易看到超时效果,我们
将超时时间设置为最小值。
1
时间单位是一分钟,并且只能是整数,如果是零或负数,那么会话就永远
不会超时。
对应例子在 08…01,为了验证 OnlineUserListener 是否能正常执行,我们可以
登录两个用户,其中一个点击注销,另一个等待一分钟,然后可以在 console
中看到输出的信息。
8。2。 使用 HttpSessionBindingListener
HttpSessionBindingListener 虽然叫做监听器,但使用方法与
HttpSessionListener 完全不同。我们实际看一下它是如何使用的。
我们的OnlineUserBindingListener 实现了HttpSessionBindingListener接口,
接口中共定义了两个方法:valueBound()和 valueUnbound(),分别对应数据绑
定,和取消绑定两个事件。
所谓对 session 进行数据绑定,就是调用 session。setAttribute()把
HttpSessionBindingListener 保存进 session 中。我们在LoginServlet。java
中进行这一步。
// 把用户名放入在线列表
80 / 148
…………………………………………………………Page 81……………………………………………………………
session。setAttribute(〃onlineUserBindingListener〃; new
OnlineUserBindingListener(username));
这就是HttpSessionBindingListener和HttpSessionListener之间的最大区别:
HttpSessionListener 只需要设置到 web。xml 中就可以监听整个应用中的所有
session。HttpSessionBindingListener 必须实例化后放入某一个 session 中,
才可以进行监听。
从监听范围上比较,HttpSessionListener 设置一次就可以监听所有 session,
HttpSessionBindingListener 通常都是一对一的。
正是这种区别成就了 HttpSessionBindingListener 的优势,我们可以让每个
listener 对应一个 username,这样就不需要每次再去 session 中读取username,
进一步可以将所有操作在线列表的代码都移入 listener,更容易维护。
valueBound()方法的代码如下:
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = event。getSession();
ServletContext application = session。getServletContext();
// 把用户名放入在线列表
List onlineUserList = (List)
application。getAttribute(〃onlineUserList〃);