1. 程式人生 > >自己動手寫web框架----1

自己動手寫web框架----1

本文可作為<<自己動手寫struts–構建基於MVC的Web開發框架>>一書的讀書筆記。
一個符合Model 2規範的web框架的架構圖應該如下:
這裡寫圖片描述

Controller層的Servlet就是一個全域性的大管家,它判斷各個請求由誰去處理。
而各個BusinessLogic就決定具體做什麼。

通過上面的圖,我們能看出來核心的元件就是那個servlet,它要處理所有的請求。
那麼我們就先在web.xml裡配置這個servlet:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>web.xml的示例</display-name> <!--控制器--> <servlet> <servlet-name
>
Servlet</servlet-name> <servlet-class>com.gd.action.GdServlet</servlet-class> </servlet> <!--攔截.do的請求--> <servlet-mapping> <servlet-name>Servlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping
>
</web-app>

很簡單,就是攔截所有.do的請求,交給com.gd.action.GdServlet來處理。
而我們的”大管家”至少要滿足下面的格式要求。

package com.gd.action;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GdServlet extends HttpServlet{
    /**
     *
     */
    private static final long serialVersionUID = -5277297442646870070L;

    public void init() throws ServletException {

      }

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        doPost(req, res);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        dodispatcher (req, res);
    }

    private void do_Dispatcher (HttpServletRequest req, HttpServletResponse res) {

    }

    public void destroy() {        
    }
}

咱們暫時先不考慮do_Dispatcher內部。這是最大的問題,咱們放到最後。
先解決周邊問題:
1 假如,我們已經生成了對應的模型層,那麼總得給傳遞模型層資料吧。資料來源於request,我們要是直接把request傳遞給某個具體的模型層,這就完全不符合Model 2規範了。因此最好的辦法就是把request中的資料剝離出來形成一個物件,再把這個物件傳遞給具體的模型層。
這個物件是什麼型別?request中的資料是以key-value的形式儲存的,那就使用HashMap吧。

 private Map<String, Object> fromRequestToMap(HttpServletRequest req){

        try {
            req.setCharacterEncoding("UTF-8");
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        Enumeration<String> e=req.getParameterNames();
        Map<String, Object> infoIn=new HashMap<String, Object>();
        while (e.hasMoreElements()) {
            String paraName = (String) e.nextElement();
            //為什麼使用getParameterValues而不是getParameter?大家自己查資料
            Object[] value=req.getParameterValues(paraName);

            if (value==null) {
                infoIn.put(paraName, "");
            }else if(value.length==1) {
                infoIn.put(paraName, value[0]);
            }else {
                infoIn.put(paraName, value);
            }
        }
        return infoIn;        
    }

2 所有的模型層應該有個共同的介面,接收上面的infoIn,處理,然後返回資料。我們看看上面,也就能猜出返回的資料也應該是HashMap。
其介面應該是這樣的:

public interface Action{
    public Map<String,Object> execute(Map<String,Object> infoIn);
}

3 我們寫一個最簡單的邏輯模型層。

package com.gc.action;

import java.util.HashMap;

public class HelloWorldAction  implements com.gd.action.Action{


    /**該方法用來實現沒有傳入動作時要處理的內容
    * @param infoIn
    * @return HashMap
    */

    private HashMap<String, Object> doInit(HashMap<String, Object> infoIn) {
        HashMap<String, Object> infoOut = infoIn;

        infoOut.put("msg", "HelloWorld");

        return infoOut;

    }

    @Override
    public HashMap<String, Object> doAction(HashMap<String, Object> infoIn) {
        // TODO Auto-generated method stub
        String action = (infoIn.get("action") == null) ? "" : (String)infoIn.get("action");
        HashMap<String, Object> infoOut = new HashMap<String, Object>() ;
        if (action.equals(""))
            infoOut = this.doInit(infoIn);
        else if (action.equals("show"))
            //....
        return infoOut;

    }
}

假定這個action的功能就只是顯示一個helloword。
4 具體的模型層已經有了,從控制層給模型層傳遞的引數型別也有了。下來就是關鍵的一步,也就是在控制層指定請求的模型。
假定我們我請求上面那個HelloWorldAction,最後的資訊返回給index.jsp那麼我們可以發出下面的請求地址:
http://localhost:8700/Struts2Demo/gc/dfd.do?logicName=HelloWorldAction&forwardJsp=index
那個dfd是個隨機的字串。
通過
String ss = req.getServletPath();
就可以獲得/gc/dif.do這個字串。
而我們的HelloWorldAction位於
package com.gc.action;
OK,反射。

 Action action=null;
    String servletPath=req.getServletPath();
    String systemPath=servletPath.split("/")[1];  //systemPath 就是gc
    String logicActionName=req.getParameter("logicName");  // logicActionName 就是HelloWorldAction
    String actionPath=getActionPath(systemPath, logicActionName);
    action=(Action) Class.forName(actionPath).newInstance();
    Map<String, Object> infoOut=action.doAction(infoIn);   

    private String getActionPath(String systemPath,String actionName){
        String actionPath="";
        if (systemPath!=null)
            actionPath="com."+systemPath+".action."+actionName;

        return actionPath;
    }

這樣一來,我們就取得了要訪問的action,並且讓呼叫了其doAction方法。
下面的任務就返回給檢視層了:

Map<String, Object> infoOut=action.doAction(infoIn);
    infoOut.put("systemPath", systemPath);
    req.setAttribute("infoOut", infoOut);
    forwards(req, res);

    private void forwards(HttpServletRequest req, HttpServletResponse res) {
        // TODO Auto-generated method stub
        Map<String, Object> infoOut=(Map<String, Object>) req.getAttribute("infoOut");
        try {
            req.getRequestDispatcher("/"+infoOut.get("systemPath")+"/jsp/"+
                    infoOut.get("forwardJsp")+".jsp").forward(req, res);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

再看檢視層:

<%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %>
<%@ page import="java.sql.*,java.util.*,javax.servlet.*,
         javax.servlet.http.*,java.text.*,java.math.*"%>

<%
    HashMap infoOut = (request.getAttribute("infoOut") == null) ? new HashMap() : (HashMap)request.getAttribute("infoOut");
    String msg = infoOut.get("msg") == null ? "" : (String)infoOut.get("msg");        
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>採用新的框架實現HelloWorld輸出</title>
<style type="text/css">
<!--
body {
    background-color: #FFFFFD;
    font-family: Verdana, "宋體";
    font-size: 12px;
    font-style: normal;
}
-->
</style>

</head>
<body leftmargin="0" topmargin="0">
<form name="form1" action="/myApp/do" method="post">

<H1><%=msg%><H1>

</form>

</body>
</html>

我們看看效果:
這裡寫圖片描述
似乎還不錯
再看看我的專案工程圖:
這裡寫圖片描述
這一節,我們很粗略地寫了一個web框架,當然這個框架還很醜陋,就能顯示個helloworld。我們在下一節在對他進行進一步的完善。