一个简单的MVC框架

一个简单的MVC框架

1、ActionMap,action name与action object之间的映射

public class ActionMap {

private Map<String, Object> actionMap = new HashMap<String, Object>();

public Object getAction(String actionName){
return actionMap.get(actionName);
}

public void initFromProperties(Configuration conf) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
System.out.println("Init actionMap from Properties " + conf.getFileName() + " ...");
Set<Entry<Object, Object>> keyValues = conf.entrySet();
for(Entry<Object, Object> keyValue : keyValues){
this.actionMap.put(keyValue.getKey().toString(), Class.forName(keyValue.getValue().toString()).newInstance());

}
System.out.println("Init actionMap success, Total size " + this.actionMap.size());
}
}

2、NormalUrl是用来解析url中的Servlet path中的action name与method name的(本来还有一个RestfulUrl的,但又不想在这里实现),约定为 http://www.unclepeng.com:XXXX/appName/actionName/methodName/...

public class NormalUrl {

public static final String  DEFAULT_METHOD_NAME = "index";

private String actionName;

private String methodName;

private int level = 2;

public String getActionName() {
return actionName;
}

public String getMethodName() {
return methodName;
}

public static NormalUrl fromServletPath(String str){
if(str.startsWith("/")){
str = str.substring(1);
}
String[] strs = StringUtils.split(str, '/', true);
NormalUrl url = new NormalUrl();
if(strs == null || strs.length == 0){
throw new IllegalArgumentException(String.format("Illegal Servlet path %s", str));
}
url.actionName = StringUtils.split(strs[0], '.', true)[0];
if(strs.length > 1){
url.methodName = StringUtils.split(strs[1], '.', true)[0];
}else{
url.methodName = DEFAULT_METHOD_NAME;
url.level = 1;
}
return url;
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}
}

3、核心DispatcherServlet,在init的时候初始化actionMap,在doGet方法中根据servlet path解析出对应的actionName与methodName,再找到对应的Action Object,然后invoke method

public class DispatcherServlet extends HttpServlet {

private static final long serialVersionUID = - 8330920476525405610L;

    public static final String INCLUDE_PATH_INFO =
        "javax.servlet.include.path_info";

public DispatcherServlet() {
super();
}

private ActionMap actionMap = new ActionMap();

public static int FILE_NOT_FOUND = 404;

public void destroy() {
super.destroy();
this.actionMap = null;
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//Split URL
String path = request.getServletPath();

NormalUrl url = NormalUrl.fromServletPath(path);
String actionName = url.getActionName();
String methodName = url.getMethodName();

//Get action by name, if not found, response 404
Object action = this.actionMap.getAction(actionName);
if(action == null){//action not found
System.err.println(String.format("Action named '%s' not found", actionName));
response.sendError(FILE_NOT_FOUND);
}

//Invoke action method, and forward if invoke result not returns null
try{
Method method = action.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
Object o = method.invoke(action, new Object[]{request, response});
if(o!= null){
if(url.getLevel() == 2){
request.getRequestDispatcher("../" + (String)o).forward(request, response);
}else{
request.getRequestDispatcher((String)o).forward(request, response);
}
return;
}
}catch(Exception e){
e.printStackTrace();
response.sendError(FILE_NOT_FOUND);
return;
}
}

public void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

public void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

public void init() throws ServletException {
        ServletConfig servletConfig = this.getServletConfig();         
        String mvcConfigFilePath = servletConfig.getInitParameter("mvc_config"); 
        Configuration conf = null;
        if(mvcConfigFilePath != null){//case 1, use properites configuration
        try {
conf = new PropertiesConfiguration(mvcConfigFilePath);
actionMap.initFromProperties(conf);
return;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
        }else{//case 2, use packages setting
        String realPath = this.getServletContext().getRealPath("/");
        String packagesPath = servletConfig.getInitParameter("action_packages");
        String[] packagePathArray = StringUtils.split(packagesPath, ',', true);
        for(String packagePath : packagePathArray){
       
        }
        }
}

}

4、web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>  
    <servlet-name>Dispatcher</servlet-name>  
    <servlet-class>com.upeng.webcommons.mvc.DispatcherServlet</servlet-class>  
    <init-param>  
        <param-name>mvc_config</param-name>  
        <param-value>mvc.properties</param-value>  
    </init-param>  
  </servlet>  
  <servlet-mapping>  
    <servlet-name>Dispatcher</servlet-name>  
    <url-pattern>*.do</url-pattern>  
  </servlet-mapping>
</web-app>

5、示例Action

public class ProductAction {

public String index(HttpServletRequest request, HttpServletResponse response){
return "product.jsp";
}
}

6、mvc.properties配置
product =com.upeng.webcommons.test.ProductAction

7、Test
http://localhost:222/webcommons/product/index.do 或者 http://localhost:222/webcommons/product.do,其中的product为action名,index为action中的方法名,当没指定方法时,默认选择action中的index方法

总结与选型过程:
1、本来想实现基于类的MVC,类似struts2,然后把对应的url parameter注入到action的field中,这样做好处是能跟servletAPI完全解耦,坏处是为每次请求以反射的方式生成一个临时的action object,效率上要打些折扣。

2、也想过实现类似mvc.net 1.0中的把action实现成sigton,然后实现基于方法的MVC把对应的url parameter注入到方法的参数里然后回调,这样做要求action中不能有重名的方法,另外在查找方法的时候要做一次遍历,稍微比直接指定method name跟parameterTypes式的查找慢了那么一点。
但是还需要把string型的url parameters转型为方法参数对应的类型效率上又打了一点点的折扣。综合考虑下便又打消了这个念头

3、这还只是一个MVC雏形,初步实现了url到action乃到action中的method之间的mapping,当然,也可以添加对参数检验,错误处理、国际化等的支持。

你可能感兴趣的:(mvc,框架,jsp,xml,servlet)