Webwork是一款优秀的WEB应用框架,在其基础之上发展而来的Struts2已经开始替代Struts作为MVC模式下的WEB框架。熟悉Webwork的程序员很容易过渡到Struts2。本文来模拟一下Webwork的URL解析,应用反射机制实现,只作为说明,当然没有webwork本身实现的完美。
Webwork默认解析的服务请求名是.action,这个过程是Servlet容器完成的,而不是框架本身,在web.xml配置<servlet-mapping>和<filer-mapping>时设置<url-pattern>即可实现,这里我们不做过多说明,仅用Servlet来模拟,配置文件使用属性配置文件properties。
首先还是回顾一下Webwork解析服务请求的方式吧。我们提交的请求以xx.action发出时,在<xwork>中配置的<action>元素中若没有method属性时,则执行的是class类中的execute()方法,若有method属性时,则执行method中规定的方法。当请求以xx!yy.action形式发出时,在<action>元素中找到class属性的指向类,在该类中执行yy()方法来响应请求。因为Webwork的Action可以是一个POJO,而且方法返回值都默认为String,则在<action>中的<result>元素中的name值和方法返回值匹配后,就转向到<result>标识的目的地址中了,这个地址当然可以是目标页面也可以是另外一个请求地址。
创建一个WEB项目,起名就叫MVC,配置如下内容:
[img]http://dl.iteye.com/upload/attachment/264773/6b110ec1-9d67-3e9c-a124-016880d4320b.jpg[/img]
在web.xml中配置上一个核心控制器和字符过滤器,很简单,如下进行即可。
这样所有已action为服务请求名的请求都被核心控制器FrontController处理了,那么只要设计好这个核心控制器就行了,那么我们就用这个核心控制器来实现MVC模式。
代码中有详细的注释,我们说一下简单的思路。这是一个Servlet,那是肯定的,因为我们已经在web.xml中声明了。那么就要覆盖父类中的doGet()和doPost()两个基本方法实现对Http请求的处理。但是这里我们也覆盖了init()方法用来初始化一些东西。可以看出是从action.properties中加载配置信息,那么这个文件中有什么呢?很简单,就是action的名和类全名,如下:
读取出这两个信息后,使用反射生成Action的实例并保存到一个HashMap中。这样就是以名/值对方式存在的了。下面就是获取urls.properties中信息了,这里面记录了跳转的地址信息,是V层的实现。如下:
init()方法解释完后,我们看看具体的servlet处理方法processRequest()。首先获取到请求的URI,然后解析这个URI看看其具体格式。分为有!号的请求和没有!号的请求,这里就是模拟webwork的实现。创建两个String遍历存放handler和method,然后匹配URI解析到的内容。进而使用反射机制获取具体的Action实例和方法。最后判断跳转到路径,然后使用request.getRequestDispatcher().forward()方法进行跳转,那么流程处理就完成了。至此控制器C的原理就说完了。
模型M就是配置的Action类,我们看一看默认Action的写法:
是不是和WebWork的Action类很相似,这里还可以操作原始的request,response对象,执行效率也很高。
其实,这也是一个小框架的简单实现,功能简单但是原理清晰,对于理解MVC模式非常有帮助。
一家之言,仅供参考,欢迎交流。
Webwork默认解析的服务请求名是.action,这个过程是Servlet容器完成的,而不是框架本身,在web.xml配置<servlet-mapping>和<filer-mapping>时设置<url-pattern>即可实现,这里我们不做过多说明,仅用Servlet来模拟,配置文件使用属性配置文件properties。
首先还是回顾一下Webwork解析服务请求的方式吧。我们提交的请求以xx.action发出时,在<xwork>中配置的<action>元素中若没有method属性时,则执行的是class类中的execute()方法,若有method属性时,则执行method中规定的方法。当请求以xx!yy.action形式发出时,在<action>元素中找到class属性的指向类,在该类中执行yy()方法来响应请求。因为Webwork的Action可以是一个POJO,而且方法返回值都默认为String,则在<action>中的<result>元素中的name值和方法返回值匹配后,就转向到<result>标识的目的地址中了,这个地址当然可以是目标页面也可以是另外一个请求地址。
创建一个WEB项目,起名就叫MVC,配置如下内容:
[img]http://dl.iteye.com/upload/attachment/264773/6b110ec1-9d67-3e9c-a124-016880d4320b.jpg[/img]
在web.xml中配置上一个核心控制器和字符过滤器,很简单,如下进行即可。
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>mvc.filters.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<servlet-name>FrontController</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>FrontController</servlet-name>
<servlet-class>mvc.ctl.FrontController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
这样所有已action为服务请求名的请求都被核心控制器FrontController处理了,那么只要设计好这个核心控制器就行了,那么我们就用这个核心控制器来实现MVC模式。
package mvc.ctl;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FrontController extends HttpServlet {
private Map actions = new HashMap();// 装资源文件中配置的action
public Map urls = new HashMap();// 装资源文件配置的url
@Override
public void init() throws ServletException {
// 读取action配置文件
ResourceBundle rb = ResourceBundle.getBundle("actions");
Enumeration keys = rb.getKeys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = rb.getString(key);
try {
// 根据资源文件的value反射获取action对象并装入HashMap
Object o = Class.forName(value).newInstance();
actions.put(key, o);
} catch (Exception e) {
e.printStackTrace();
}
}
// 读取目的地址配置文件
ResourceBundle url = ResourceBundle.getBundle("urls");
keys = url.getKeys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = url.getString(key);
urls.put(key, value);
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 从请求中获取URI
String requestUri = request.getRequestURI();
// 根据最后一个/截取请求地址(包含.action)
String actionurl = requestUri.substring(
requestUri.lastIndexOf('/') + 1, requestUri.length());
// 截取.action前的有效数据
actionurl = actionurl.substring(0, actionurl.indexOf(".action"));
// 创建保存处理类和方法的变量
String action = "";
String method = "";
if (actionurl.indexOf("!") >= 0) {// 有!号的请求这样处理
action = actionurl.substring(0, actionurl.indexOf("!"));
method = actionurl.substring(actionurl.indexOf("!") + 1);
} else {// 没有!号时则默认执行execute()方法
action = actionurl;
method = "execute";
}
// 根据截取的action名获得存放在HashMap中的action对象
Object handler = null;
handler = actions.get(action);
if (handler == null) {// 没有找到时默认执行default配置的action
handler = actions.get("default");
method = "execute";
}
// 存在时,获取Class实例
Class handlerClass = handler.getClass();
Method executor = null;
// 设置请求处理结束的派发地址
String toJump = "index";
try {
// 反射获取执行方法,由于是Servlet,所以参数是HttpServletRequest和HttpServletResponse
executor = handlerClass.getMethod(method, new Class[] {
HttpServletRequest.class, HttpServletResponse.class });
} catch (Exception e) {
e.printStackTrace();
toJump = "frameerror";
}
try {
// 利用反射机制执行方法,方法调用结束,返回值都是String类型的
toJump = (String) executor.invoke(handler, new Object[] { request,
response });
} catch (Exception e) {
e.printStackTrace();
toJump = "frameerror";
}
// 转发处理结束以后的地址
request.getRequestDispatcher(getURL(toJump)).forward(request, response);
}
/**
* 从HashMap中获取URL的方法
*/
public final String getURL(String url) {
return (String) urls.get(url);
}
}
代码中有详细的注释,我们说一下简单的思路。这是一个Servlet,那是肯定的,因为我们已经在web.xml中声明了。那么就要覆盖父类中的doGet()和doPost()两个基本方法实现对Http请求的处理。但是这里我们也覆盖了init()方法用来初始化一些东西。可以看出是从action.properties中加载配置信息,那么这个文件中有什么呢?很简单,就是action的名和类全名,如下:
base=mvc.action.BaseAction
default=mvc.action.DefaultAction
读取出这两个信息后,使用反射生成Action的实例并保存到一个HashMap中。这样就是以名/值对方式存在的了。下面就是获取urls.properties中信息了,这里面记录了跳转的地址信息,是V层的实现。如下:
index=index.jsp
frameerror=frameerror.jsp
init()方法解释完后,我们看看具体的servlet处理方法processRequest()。首先获取到请求的URI,然后解析这个URI看看其具体格式。分为有!号的请求和没有!号的请求,这里就是模拟webwork的实现。创建两个String遍历存放handler和method,然后匹配URI解析到的内容。进而使用反射机制获取具体的Action实例和方法。最后判断跳转到路径,然后使用request.getRequestDispatcher().forward()方法进行跳转,那么流程处理就完成了。至此控制器C的原理就说完了。
模型M就是配置的Action类,我们看一看默认Action的写法:
package mvc.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DefaultAction {
public String execute(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("msg", "未定义操作");
return "error";
}
}
是不是和WebWork的Action类很相似,这里还可以操作原始的request,response对象,执行效率也很高。
其实,这也是一个小框架的简单实现,功能简单但是原理清晰,对于理解MVC模式非常有帮助。
一家之言,仅供参考,欢迎交流。