Struts、Ajax、Restful、Web Service 狂想曲框架实现篇(三)

      在第一篇时就说过框架要在URL上作文章,是的,本文就框架怎样充分利用url上作尽可能详细的说明。

      做web开发的不可能对url陌生,早在web1.0时代,url作为统一资源定位符,在对web中资源的如何获得上起到巨大作用。不论用户请求的时静态页面或者是各种图片、脚本文件,通过url总能从web网站获取要访问的资源。Web2.0更是常常使用url作为get请求时参数的传递,如http://xxx.xxx.xxx/xxx.jsp?user=admin。以及近几年很火restful web service 摒弃soap而使用url传递请求参数 都说明合理利用url的可行和流行。

      当然不止是使用了url就算好的实践,而是能够做到优雅的使用,保证层次分明和整体的简洁,这才算是好的方式,这也正是本框架对使用url 所追求的目标。

      首先来看几个例子:

http://www.cnblogs.com/p2
http://www.xxx.com/index.do?page=2

http://www.xxx.com/product/
http://www.xxx.com/channel.do?channel=product

http://www.xxx.com/product/mobile
http://www.xxx.com/channel.do?channel=product&&subChannel=mobile

      相信各位看官不用我说也能明白,这几组的实现肯定第一种实现的方式更佳。抛开它能屏蔽服务器端使用的技术这一特性不说,它还能够更好地说明动态网站的层次结构,让用户在访问时能明确知道在网站的什么位置,而不会觉得是陷入了一个迷宫。

      当然上面列举的例子是网站前端所使用的url表现方式,因为表现方式可以多种多样,个人喜好不同,本框架在设计时没有给指定前端url的表现方式,而是定义接口,把这个权利留给使用的用户。框架将考虑更多 通用性的东西而不是 个性 自由的东西。

 

      下面对框架里默认使用的url Router AMPPathRouter做详细的介绍,包括设计的思想和实现的方式。首先AMPPathRouter的用途定位为后台使用。为了理解快速的理解它的工作原理,先来和struts做一下对比。

      Struts关于请求的配置:

 

<action name="login" class="com.lscmjx.action.LoginAction" method="login">
<result name="success">
main.jsp
</result>
<result name="failure">
login.jsp
</result>
</action>

    它提交的url会是http://xxx.xxx.xxx/login,访问web服务器时会把此url传递到struts框架交给它处理,之后struts会在struts.xml中寻找login的相关的配置,像上面例子,struts会找到LoginAction的类,并且调用其login的方法。

      写到这里,我请问这是最好的方式吗?当然不是,至少我在使用struts时就认为这是相当撇脚的设计。上面例子只是列举一个login方法,假如一个系统中要对后台调用的方法是100个,那岂不是就需要在struts.xml中写100个与之类似的配置。想想都头大,这样繁琐的工作,应该是由框架自己去处理,而不是人工给配置。

 

      再来看实现相同功能的Unicorn web框架的配置。

 

<action class="com.mh.action.UserAction"></action>

      当然提交的url肯定需要包含多一些的信息,来保证能通过url正确调用框架Action里的方法。这里提交的url方式:http://xxx.xxx.xxx/UserAction/login/

      通过在url里附加调用的Action类的信息,可以省略为不同的方法都在xml里配置的麻烦。假如UserAction里有100个方法,框架也只需这一行的配置。

      有了大体的认识之后,来看框架的核心部分AMPPathRouter的具体实现。

 

/**
* 匹配规则为:
* 1、符合/Action/method/param格式,
* 2、并且Action在actionMap中的确存在
* 3、method在此Action中存在
*/
@Override
public boolean route(String relativeUri, UrlFilter urlFilter) {
Map<String, String> actionMap = urlFilter.getActionMap();
Pattern pattern = Pattern.compile("^/\\w+/\\w+/\\S*");
Matcher matcher = pattern.matcher(relativeUri);
// 检查url是否是此Router类要处理的,/Action/Method/Param 格式的将会被检查合格,返回true
if(matcher.matches()) {
String[] params = relativeUri.split("/");
String actionName = params[1];
String fullActionName = actionMap.get(actionName);
if(null == fullActionName) {
return false;
}
try {
Class<?> clz = Class.forName(fullActionName);
ActionSupport actionSupport = (ActionSupport) clz.newInstance();
String actionMethodName = params[2];
Method[] methods = clz.getMethods();
for(int i = 0; i < methods.length; i++) {
Method method = methods[i];
String methodName = method.getName();
if(methodName.equals(actionMethodName)) {
// 拦截Action/Method/Param方式的请求,并构建ActionSupport类的属性
if(params.length > 3) {
this.boxingRequest(urlFilter.getRequest(), params[3]);
}
this.initActionSupport(actionSupport, urlFilter);
return this.invokeActionMethod(actionSupport, method, urlFilter);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return false;
}
// 调用Action里的方法
private boolean invokeActionMethod(ActionSupport actionSupport, Method method, UrlFilter urlFilter) {
try {
String result = (String) method.invoke(actionSupport, new Object[] {});
if (null == result || ActionSupport.AJAX.equals(result) || ActionSupport.FORWARD.equals(result) || ActionSupport.WEB_SERVICE.equals(result)) {
return true;
}
if(ActionSupport.REDIRECT.equals(result)) {
urlFilter.getResponse().sendRedirect(result);
return true;
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 把url中得param加入到request的attribute里
*
@param request
*
@param parameter
*/
private void boxingRequest(HttpServletRequest request, String parameter) {
String[] parameters = parameter.split("&");
for (int i = 0; i < parameters.length; i++) {
String param = parameters[i];
String[] key_value = param.split("=");
if(key_value.length == 2) {
request.setAttribute(key_value[0], key_value[1]);
}
}
}
/**
* 初始化ActionSupport类中所需的request、response、session、application等对象
*
@param obj
*
@param urlFilter
*/
private void initActionSupport(ActionSupport actionSupport, UrlFilter urlFilter) {
actionSupport.setRequest(urlFilter.getRequest());
actionSupport.setResponse(urlFilter.getResponse());
actionSupport.setSession(urlFilter.getSession());
actionSupport.setApplication(urlFilter.getApplication());
}

 

      这便是AMPPathRouter的全部内容,其中在把请求分发到ActionSupport的子类 并调用相关方法时 是通过反射实现,其他地方地方都是相当容易理解的。

      空说无凭,把框架应用到实战中才是硬道理:

 

      好了,下一篇介绍Action 和 json。

posted @ 2011-11-25 18:18  mac的学习笔记  阅读(1954)  评论(0编辑  收藏