三、页面跳转封装
【接上】在《【斗医】【4】Web应用开发50天》中页面跳转封装只做了如下工作:名称为action的Servlet启动时,解析/WEB-INF/config下各业务模块的XXX-action.xml,并把其业务封装为FrameBusiness对象缓存到全局变量Map中:
接下来要完成如下功能:
Web容器(Tomcat)接受到HTTP请求后,Web容器把HTTP转换为HttpServletRequest对象,然后根据请求名称匹配规则找到相应的Servlet,调用servlet-class对应类的doGet()或doPost()方法,在该方法中根据请求名称从全局缓存(FrameCache.businessMap)中读取业务对象(FrameBusiness),由FrameBusiness对象跳转到相应的页面。
1、配置名称为action的Servlet。
打开D:\medical\war\WEB-INF\web.xml文件,在里面填充如下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<
web-app
>
<
servlet
>
<
servlet-name
>action</
servlet-name
>
<
servlet-class
>com.medical.frame.FrameLauncher</
servlet-class
>
<
load-on-startup
>1</
load-on-startup
>
</
servlet
>
<
servlet-mapping
>
<
servlet-name
>action</
servlet-name
>
<
url-pattern
>*.act</
url-pattern
>
</
servlet-mapping
>
<
welcome-file-list
>
<
welcome-file
>index.html</
welcome-file
>
</
welcome-file-list
>
</
web-app
>
|
【备注】:该内容在前面已配置过,这里再强调一次一方面是理顺思路,另一方面是强调Web容器处理HTTP请求的流程。
2、把HttpServletRequest请求均交给get处理
打开D:\medical\src\com\medical\frame\FrameLauncher.java,把doPost()处理交给doGet():
1
2
3
4
5
|
@Override
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException
{
doGet(request, response);
}
|
3、为了保持FrameLauncher功能的简洁性,这里定义一个FrameDispatcher类,FrameLauncher.doGet()的业务逻辑交给FrameDispatcher处理:
I、定义com.medical.frame.FrameDispatcher.java类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
FrameDispatcher
{
private
HttpServlet servlet =
null
;
/**
* 构造器
*/
public
FrameDispatcher(HttpServlet servlet)
{
this
.servlet = servlet;
}
public
void
process(HttpServletRequest request, HttpServletResponse response)
throws
FrameException
{
// TODO
}
}
|
II、FrameLauncher.doGet()交给FrameDispatcher对象处理
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Override
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException
{
FrameDispatcher dispatcher =
new
FrameDispatcher(
this
);
try
{
dispatcher.process(request, response);
}
catch
(FrameException e)
{
logger.error(e.getErrorDesc(), e);
}
}
|
4、定义FrameDispatcher.getRequestName(HttpServletRequest)方法从HttpServletRequest对象中解析出请求名称(比如从/medical/index.act中获取index)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
* 由HttpServletRequest对象获取请求名称
*/
private
String getRequestName(HttpServletRequest request)
{
String name = request.getRequestURI();
if
(name ==
null
)
{
return
null
;
}
if
(name.endsWith(FrameConstant.BUS_NAME_POSTFIX) ==
false
)
{
return
null
;
}
int
headIndex = name.lastIndexOf(
"/"
) +
1
;
int
rearIndex = name.lastIndexOf(FrameConstant.BUS_NAME_POSTFIX);
return
name.substring(headIndex, rearIndex);
}
|
5、修改FrameDispatcher.process()方法,其功能是由业务名称获取对应的业务对象,然后由业务对象的forward来决定跳转页面
6、下面初步测试一下:
I、修改D:\medical\war\index.html文件,该文件加载完就跳转到index.act
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE HTML>
<
html
>
<
head
>
<
title
>medical</
title
>
<
script
type
=
"text/javascript"
>
top.location="index.act";
</
script
>
</
head
>
</
html
>
|
II、修改D:\medical\war\WEB-INF\config\sm\system-action.xml文件
1
2
3
4
5
6
|
<
business
name
=
"index"
mustlogin
=
"false"
>
<
forward
>
<
path
name
=
"success"
path
=
"/module/main/main.html"
/>
<
path
name
=
"failure"
path
=
"/module/main/main.html"
/>
</
forward
>
</
business
>
|
当系统加载时名称为action的Servlet会把该文件读入全局缓存。访问index.html时执行top.location="index.act",action接受到"*.act"就执行FrameDispatcher.process()方法,从而跳转到/module/main/main.html页面
III、在D:\medical\war\module\main\下创建main.html文件,这里只是为了测试,所以main.html只是输出一个<h1>This is main page!</h1>标题:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE HTML>
<
html
>
<
head
>
<
title
>medical</
title
>
</
head
>
<
body
>
<
h1
>This is main page!</
h1
>
</
body
>
</
html
>
|
IV、启动Tomcat服务,在浏览器中输入http://localhost:8080/medical,会发现页面直接把<h1>This is main page!</h1>显示出来了,见下图:
【备注】:细心的读者可能发现了页面进入了main.html,但地址却依旧停留在index.act处,见上图。其中原因请大家在谷歌上搜索一下“JSP forward”,有文章会给出详细说明。
7、再仔细考虑一个场景:在跳转到页面A之前,(1)有可能需要把当前页面的数据保存在数据库中,(2)或者需要进行一些逻辑判断,符合条件才能进入A页面,不符合则不能进入A页面。这就是当时XXX-action.xml中业务定义"business-class"的原因,但我们的FrameDispatcher.process()并没有这方面的处理,现在进行修改:
I、定义缺省跳转动作执行类com.medical.frame.FrameDefaultAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
/**
* 斗医系统业务缺省跳转处理器
*
* @author qingkechina
*/
public
class
FrameDefaultAction
{
/**
* 请求对象
*/
protected
HttpServletRequest request =
null
;
/**
* 响应对象
*/
protected
HttpServletResponse response =
null
;
/**
* 会话对象
*/
protected
HttpSession session =
null
;
public
void
setRequest(HttpServletRequest request)
{
this
.request = request;
}
public
void
setResponse(HttpServletResponse response)
{
this
.response = response;
}
public
void
setSession(HttpSession session)
{
this
.session = session;
}
/**
* 读取Request对象中的参数
*/
public
String getParameter(String param)
{
return
request.getParameter(param);
}
/**
* 缺省响应动作
*/
public
String execute()
throws
FrameException
{
return
null
;
}
}
|
【备注】:这里没有使用接口或抽象类,建议读者在自已实现该功能时最好能抽象出接口,这样做的好处可参见设计模式相关内容
II、定义动作工厂,可以根据XXX-action.xml中的业务business-class名称把相关的跳转动作类反射出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package
com.medical.frame;
import
com.medical.frame.constant.FrameConstant;
import
com.medical.frame.constant.FrameErrorCode;
/**
* 斗医系统业务跳转动作工厂类
*
* @author qingkechina
*/
public
class
FrameActionFactory
{
private
static
FrameActionFactory instance =
new
FrameActionFactory();
private
FrameActionFactory()
{
}
public
static
FrameActionFactory getInstance()
{
return
instance;
}
/**
* 反射跳转动作实现类
*/
public
FrameDefaultAction implement(String className)
throws
FrameException
{
if
(className ==
null
|| className.trim().length() ==
0
)
{
className = FrameConstant.FORWARD_DEFAULT_ACTION;
}
try
{
return
(FrameDefaultAction) Class.forName(className).newInstance();
}
catch
(Exception e)
{
throw
new
FrameException(FrameErrorCode.JAVA_REFLECT_ERROR, e);
}
}
}
|
III、修改FrameDispatcher.process()方法,在其中增加跳转动作逻辑部分的处理(即下图红框部分):
8、测试跳转功能:
I、打开D:\medical\war\WEB-INF\config\sm\system-action.xml文件,对"index.act"动作指定逻辑响应处理类Demo
1
2
3
4
5
6
|
<
business
name
=
"index"
mustlogin
=
"false"
business-class
=
"com.medical.frame.forward.Demo"
>
<
forward
>
<
path
name
=
"success"
path
=
"/module/main/main.html"
/>
<
path
name
=
"failure"
path
=
"/module/main/main.html"
/>
</
forward
>
</
business
>
|
II、定义com.medical.frame.forward.Demo类,该类继承FrameDefaultAction类,并重写execute()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package
com.medical.frame.forward;
import
com.medical.frame.FrameDefaultAction;
import
com.medical.frame.FrameException;
public
class
Demo
extends
FrameDefaultAction
{
@Override
public
String execute()
throws
FrameException
{
// 这里可以处理一些逻辑
System.out.println(
"===================这里可以进行逻辑处理"
);
// 逻辑处理完后跳转到相应页面
return
"login.act"
;
}
}
|
从代码上可以看出,这里逻辑处理完之后,就直接跳转到login.act对应的页面
III、打开D:\medical\war\WEB-INF\config\sm\system-action.xml文件,修改"login.act"对应的业务配置
1
2
3
4
5
6
|
<
business
name
=
"login"
mustlogin
=
""
>
<
forward
>
<
path
name
=
"success"
path
=
"/module/login/login.html"
/>
<
path
name
=
"failure"
path
=
"/module/login/login.html"
/>
</
forward
>
</
business
>
|
IV、在D:\medical\war\module\login下创建login.html文件,填充如下内容:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE HTML>
<
html
>
<
head
>
<
title
>medical</
title
>
</
head
>
<
body
>
<
h1
>This is Login page!</
h1
>
</
body
>
</
html
>
|
V、启动Tomcat后在浏览器中输入http://localhost:8080/medical回车,查看页面,页面上输出“This is Login page!”,这说明页面是跳转到了login.html上,如图:
【备注】:上面的测试也说明当业务配置了business-class,则相应动作执行类跳转动作的优先级要高于<forward>
OK,到这里跳转封装的基本功能已完成,可能会有人问:“只是一个跳转,使用HTML的<a>标签,岂不是更简单,何苦折腾这么一通呢?”
有这个疑问是一件好事情,说明大家都进行了思考。正如上面所说,一些逻辑性处理需要在服务端进行(如有些场景需要根据逻辑判断决定跳转到哪个页面,若纯使用WEB前端处理,显的客户端比较胖或者叫富客户端),另外把眼光放远一些,试想Struts做了哪些事情呢?除了过滤器之外无非也是逻辑判断,数据加工后的页面显示。
下面我们再理顺一下思路:
(1)启动Tomcat时,会加载web.xml,根据web.xml把名称为action的Servlet拉起
(2)名称为action的Servlet启动时,根据其配置规则反射FrameLauncher类,并调用它的init()方法
(3)在init()方法中会读取运行环境中的XXX-action.xml文件,解析该文件后把动作名及动作名对应的业务对象FrameBusiness缓存入全局变量
(4)当在浏览器中输入http://localhost:8080/medical时,Tomcat根据web.xml配置首先加载index.html
(5)在index.html加载完毕后直接使用top.location="index.act"跳转到index.act
(6)tomcat服务器一看到是.act结尾的请求,就使用FrameLauncher的doGet()方法来处理
(7)FrameLauncher.doGet()委托给FrameDispatcher.process(),在process()中它根据index.act在全局缓存中查找FrameBusiness
(8)首先看一下FrameBusiness是否有class-name,若有就获取逻辑处理类指定的路径;若没有就使用forward指定的路径
(9)然后把该路径交给HttpServlet进行具体的页面跳转
附件:http://down.51cto.com/data/2363687
本文转自qingkechina 51CTO博客,原文链接:http://blog.51cto.com/qingkechina/1317441,如需转载请自行联系原作者