1. 程式人生 > >自定義MyStruts框架

自定義MyStruts框架

前言

通過之前的傳統mvc開發jsp+servlet模式引入struts框架。
首先通過案例來分析之前傳統開發的不足之處。

案例

功能:使用者登陸、註冊
登陸成功 – 首頁頁面
登入失敗–登入頁面
註冊成功 – 登陸頁面

採用mvc設計模式
Entity+Dao+service+Servlet+jsp

其中servlet作為MVC的Controller,無非就是三個步驟:
1.得到web層的資料、封裝到JavaBean
2.呼叫Service的邏輯程式碼
3.跳轉到相對應的JSP頁面

開發程式碼

UserDao.java

public class UserDao {
    // 模擬登陸
    public User login(User user){
        if ("tom".equals(user.getName()) && "888".equals(user.getPwd()) ){
            // 登陸成功
            return user;
        }
        // 登陸失敗
        return null;
    }

    // 模擬註冊
    public void register(User user) {
        System.out
.println("註冊成功:使用者," + user.getName()); } }

UserService.java

private UserDao ud = new UserDao();

    // 模擬登陸
    public User login(User user){
        return ud.login(user);
    }

    // 模擬註冊
    public void register(User user) {
        ud.register(user);
    }

LoginServlet.java

public class
LoginServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //封裝屬性 String name = request.getParameter("name"); String pwd = request.getParameter("pwd"); User user = new User(name,pwd); //業務操作 UserService userService = new UserService(); User u = userService.login(user); //跳轉頁面 if(u!=null){ //跳轉到首頁 request.getSession().setAttribute("userInfo", u); response.sendRedirect(request.getContextPath()+"/index.jsp"); }else{ //跳轉到註冊頁面 request.getRequestDispatcher("/register.jsp").forward(request, response); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }

RegisterServlet.java

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

        //封裝屬性
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");
        User user  = new User(name,pwd);

        //業務操作
        UserService userService = new UserService();
        userService.register(user);

        //跳轉頁面
        response.sendRedirect(request.getContextPath()+"/login.jsp");
    }

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

        doGet(request, response);
    }

分析:
通過上面程式碼簡單實現了使用者登入和註冊功能。
傳統mvc開發不足:
1. 跳轉程式碼寫死,不靈活
2. 每次都去寫servlet,web.xml中配置servlet!
(配置目的: 請求, Servlet處理類)
3.每個業務請求操作都要寫一個servlet,導致servlet過多

這時的Servlet主要起到控制的作用,如果每次都要封裝資料,呼叫service方法,處理某些業務,那麼servlet會顯得十分龐大。這時我們需要優化servlet,抽取出通用的部分,只讓它控制方法的呼叫和跳轉頁面。因此要把其中的操作封裝到一個action類中。每個servlet對應一個action類。

改造servlet+action

新增LoginAction.java

/**
 * Action表示動作類 1. 一個servlet對應一個action 2. action中負責處理具體的請求 
 */
public class LoginAction {
    public Object login(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Object uri = null;
        // 封裝屬性
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");
        User user = new User(name, pwd);

        // 業務操作
        UserService userService = new UserService();
        User u = userService.login(user);

        // 跳轉頁面
        if (u != null) {
            // 跳轉到首頁
            request.getSession().setAttribute("userInfo", u);
            uri = "/index.jsp";
        } else {
            // 跳轉到登入頁面
            uri = request.getRequestDispatcher("/login.jsp");
        }
        return uri;
    }
}

修改LoginSerlet.java–只起到呼叫業務方法,轉發控制功能

// 建立Action物件,呼叫登陸方法
        LoginAction loginAction = new LoginAction();
        Object uri = loginAction.login(request, response);
        // 跳轉
        if (uri instanceof String) {
            response.sendRedirect(request.getContextPath() + uri.toString());
        } else {
            ((RequestDispatcher) uri).forward(request, response);
        }

RegisterAction.java

public class RegisterAction {
    public Object register(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        Object uri;

        // 1. 獲取請求資料,封裝
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");
        User user = new User();
        user.setName(name);
        user.setPwd(pwd);

        // 2. 呼叫Service
        UserService userService = new UserService();
        userService.register(user);
        // 3. 跳轉
         uri = request.getRequestDispatcher("/login.jsp");
         return uri;

    }
}

RegisterServlet.java

RegisterAction registerAction = new RegisterAction();
        Object uri = registerAction.register(request, response);

        // 跳轉
        if (uri instanceof String) {
            response.sendRedirect(request.getContextPath() + uri.toString());
        } else {
            ((RequestDispatcher)uri).forward(request, response);
        }

分析:
從LoginServlet和RegisterServlet中,可以看出兩個Servlet,程式碼非常相似,只是通過不同請求,呼叫service中不同的方法,來實現頁面跳轉的。那麼,我們要想把兩個servlet合併成一個,來處理不同的請求。只需要知道請求的uri,例如:mystruts/login.action.我們通過解析uri得到login,然後根據login來找到不同的action,呼叫action中的login方法即可。如何找出指定的action呢,如果寫到程式碼裡,用if來判斷那麼不利於維護。我們可以寫在xml配置檔案中,在xml中定義不同的action,每個action定義多個處理請求的方法,然後利用反射技術來實現,根據使用者不同請求,呼叫不同的方法,實現跳轉到指定的頁面。

自定義mystruts

我們需要改造傳統的servlet開發方式,因此需要定義如下的xml配置檔案儲存action中方法,和指定的跳轉頁面。
mystruts.xml

<?xml version="1.0" encoding="UTF-8"?>
<mystruts>
    <package>
        <!-- 配置請求路徑,與處理action類的關係 -->
        <!-- 
            1. 請求路徑與處理Action的關係
                 /login = LoginAction                          login
                        success = /index.jsp                     登陸成功(重定向)
                        loginFaild  = /login.jsp                 登陸失敗           
         -->
        <action name="login" class="nwpu.geeker.framework.action.LoginAction" method="login">
            <result name="loginSuccess" type="redirect">/index.jsp</result>
            <result name="loginFaild">/login.jsp</result>
        </action>

        <action name="register" class="nwpu.geeker.framework.action.RegisterAction" method="register">
            <result name="registerSuccess">/login</result>
        </action>       
    </package>
</mystruts>

在xml檔案中指定了使用者請求uri,處理請求的Action.java,和呼叫方法以及結果顯示檢視。

我們還需要以下類來讀取配置檔案的資訊,通過反射技術來呼叫方法。
ActionMapping.java————主要封裝xml配置檔案中一個action的詳細資訊,包括:請求路徑名稱,處理請求的action類名,處理呼叫的方法,該action全部的檢視集合。
Result.java 主要封裝一個結果檢視資訊。包括指定跳轉的name,跳轉的方式(轉發、重定向等),跳轉的頁面。
ActionMappingManager.java—主要是載入xml配置檔案,儲存全部的action集合。
ActionServlet.java—–xml配置檔案中所有action共有一個servlet,根據使用者不同請求,呼叫不同action方法,實現頁面跳轉。

Resulet.java

/**
 * 封裝結果檢視
 * <result name="success" type="redirect">/index.jsp</result>
 */
public class Result {

    // 跳轉的結果標記
    private String name;
    // 跳轉型別,預設為轉發; "redirect"為重定向
    private String type;
    // 跳轉的頁面
    private String page;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getPage() {
        return page;
    }
    public void setPage(String page) {
        this.page = page;
    }

}

ActionMapping.java

/**
 * 封裝action節點
 */
public class ActionMapping {

    // 請求路徑名稱
    private String name;
    // 處理aciton類的全名
    private String className;
    // 處理方法
    private String method;
    // 結果檢視集合
    private Map<String,Result> results;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getClassName() {
        return className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public Map<String, Result> getResults() {
        return results;
    }
    public void setResults(Map<String, Result> results) {
        this.results = results;
    }   
}

ActionMappingManager.java

/**
 * 載入配置檔案, 封裝整個mystruts.xml
 */
public class ActionMappingManager {

    // 儲存全部action的集合
    private Map<String,ActionMapping> allActions ;

    public ActionMappingManager(){
        allActions = new HashMap<String,ActionMapping>();
        // 初始化
        this.init();
    }

    /**
     * 根據請求路徑名稱,返回Action的對映物件
     * @param actionName   當前請求路徑
     * @return             返回配置檔案中代表action節點的AcitonMapping物件
     */
    public ActionMapping getActionMapping(String actionName) {
        if (actionName == null) {
            throw new RuntimeException("傳入引數有誤,請檢視struts.xml配置的路徑。");
        }

        ActionMapping actionMapping = allActions.get(actionName);
        if (actionMapping == null) {
            throw new RuntimeException("路徑在struts.xml中找不到,請檢查");
        }
        return actionMapping;
    }

    // 初始化allActions集合
    private void init() {
        /********DOM4J讀取配置檔案***********/
        try {
            // 1. 得到解析器
            SAXReader reader = new SAXReader();
            // 得到src/mystruts.xml  檔案流
            InputStream inStream = this.getClass().getResourceAsStream("/mystruts.xml");
            // 2. 載入檔案
            Document doc = reader.read(inStream);

            // 3. 獲取根
            Element root = doc.getRootElement();

            // 4. 得到package節點
            Element ele_package = root.element("package");

            // 5. 得到package節點下,  所有的action子節點
            List<Element> listAction = ele_package.elements("action");

            // 6.遍歷 ,封裝
            for (Element ele_action : listAction) {
                // 6.1 封裝一個ActionMapping物件
                ActionMapping actionMapping = new ActionMapping();
                actionMapping.setName(ele_action.attributeValue("name"));
                actionMapping.setClassName(ele_action.attributeValue("class"));
                actionMapping.setMethod(ele_action.attributeValue("method"));

                // 6.2 封裝當前aciton節點下所有的結果檢視
                Map<String,Result> results = new HashMap<String, Result>();

                // 得到當前action節點下所有的result子節點
                 Iterator<Element> it = ele_action.elementIterator("result");
                 while (it.hasNext()) {
                     // 當前迭代的每一個元素都是 <result...>
                     Element ele_result = it.next();

                     // 封裝物件
                     Result res = new Result();
                     res.setName(ele_result.attributeValue("name"));
                     res.setType(ele_result.attributeValue("type"));
                     res.setPage(ele_result.getTextTrim());

                     // 新增到集合
                     results.put(res.getName(), res);
                 }

                // 設定到actionMapping中
                actionMapping.setResults(results);

                // 6.x actionMapping新增到map集合
                allActions.put(actionMapping.getName(), actionMapping);
            }


        } catch (Exception e) {
            throw new RuntimeException("啟動時候初始化錯誤",e);
        }
    }
}

ActionServlet.java

/**
 * 核心控制器,此專案只有這一個servlet
 * 1. 攔截所有的*.action為字尾的請求
 * 2. 請求:http://localhost:8080/mystruts/login.action
 *        http://localhost:8080/mystruts/register.action
 */
public class ActionServlet extends HttpServlet{

    private ActionMappingManager actionMappingManager;

    // 只執行一次  (希望伺服器啟動時候執行)
    @Override
    public void init() throws ServletException {
        System.out.println("ActionServlet.init()");
        actionMappingManager = new ActionMappingManager();
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try {
            // 1. 獲取請求uri, 得到請求路徑名稱   【login】
            String uri = request.getRequestURI();
            // 得到 login
            String actionName=uri.substring(uri.lastIndexOf("/")+1, uri.indexOf(".action"));

            // 2. 根據路徑名稱,讀取配置檔案,得到類的全名   【cn..action.LoginAction】
            ActionMapping actionMapping = actionMappingManager.getActionMapping(actionName);
            String className = actionMapping.getClassName();

            // 當前請求的處理方法   【method="login"】
            String method = actionMapping.getMethod();

            // 3. 反射: 建立物件,呼叫方法; 獲取方法返回的標記
            Class<?> clazz = Class.forName(className);
            Object obj = clazz.newInstance();  //LoginAction loginAction = new LoginAction();
            Method m = clazz.getDeclaredMethod(method, HttpServletRequest.class,HttpServletResponse.class );
//注意這裡必須是HttpServletRequest.class,不能是request.getClass(),不能使執行時傳遞的//class
            // 呼叫方法返回的標記
            String returnFlag =  (String) m.invoke(obj, request, response);

            // 4. 拿到標記,讀取配置檔案得到標記對應的頁面 、 跳轉型別
            Result result = actionMapping.getResults().get(returnFlag);
            // 型別
            String type = result.getType();
            // 頁面
            String page = result.getPage();

            // 跳轉
            if ("redirect".equals(type)) {
                response.sendRedirect(request.getContextPath() + page);
            } else {
                request.getRequestDispatcher(page).forward(request, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}

Web.xml
注意:要在啟動時執行servlet初始化方法載入mystruts.xml檔案

 <!-- 核心控制器 -->
  <servlet>
    <servlet-name>ActionServlet</servlet-name>
    <servlet-class>nwpu.geeker.framework.ActionServlet</servlet-class>
    <!-- 啟動時候執行servlet初始化方法 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>ActionServlet</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>

總結:

由於傳統web的servlet控制器模組存在弊端:
一個專案需要建立非常多的Servlet
跳轉的頁面程式碼寫死了,不利於維護。改變需求的時候需要更改更多地程式碼
因此struts就應運而生了,本文主要簡單模擬Struts的開發流程

使用一個ActionServlet核心控制器來管理全部的Web請求,自定義XML配置檔案,儲存所有action和result資訊,然後讀取配置檔案。通過uri來判斷要呼叫哪個Action中的方法,然後根據呼叫方法的返回值,確定跳轉頁面。

相關推薦

定義MyStruts框架

前言 通過之前的傳統mvc開發jsp+servlet模式引入struts框架。 首先通過案例來分析之前傳統開發的不足之處。 案例 功能:使用者登陸、註冊 登陸成功 – 首頁頁面 登入失敗–登入頁面 註冊成功 –

java 中的 定義viewUtils框架

exce stack wid policy his tools line idg 字節碼 在前面學習的基礎上,我們自己編寫一個ViewUtils註解框架類,不清楚的原理看前面的源代碼 package im.weiyuan.com.viewutils; import

定義註解框架的那些事

構造 -i 靜態 cti tails cas 數組 講解 調用 一、前言 距離上次更新已過一個半月,工作太忙也不是停更的理由。我這方面做得很不好,希望大家給予監督。首先會講解【編譯期資源註入】,接著是【下拉刷新註入】(通過註解來實現下拉刷新功能),最後打造一款【特色的註解框

簡單封裝定義MVC框架

ddk dp2 hce gho pku vuex oier ont atrm 自定義Mvc框架結構及其使用方法 一,什麽是MVC框架 MVC框架全名是model(模型)controller(控制器)view(視圖文件)所構成的一種開發框架,是一種典型的軟件設計典範,用一種業

定義xUtils框架

const enum list 解釋 policy nbsp 利用 ext.get 生命 xUtils是基於Afinal開發的目前功能比較完善的一個Android開源框架,最近又發布了xUtil3.0,在增加新功能的同時又提高了框架的性能。它的功能很強大,但是有時候我們只需

python web框架【補充】定義web框架

數據大小 路徑 .py 用戶 ipa clr 接受 values 規範 http協議 HTTP簡介 HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本

ideat使用struts2之定義MVC框架

clu src pen mod files 文件導入 exc form over 今天我學習了自定義一個簡單的MVC框架,這個我們首先要知道什麽是MVC框架! MVC框架: MVC全名是Model View Controller,是模型(model)-視圖(view)-

定義MVC框架之工具類-模型類

else pri 數據庫連接 執行 field date http one www. 截止目前已經改造了5個類: ubuntu:通過封裝驗證碼類庫一步步安裝php的gd擴展 自定義MVC框架之工具類-分頁類的封裝 自定義MVC框架之工具類-文件上傳類 自定義MVC框

定義web框架

長度 說明 enc 分享 size mys 字符 語法錯誤 manage http協議 HTTP簡介 HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本

JavaScript定義勻速運動框架

最近寫到無縫輪播,所以用到了運動框架,就自己寫了一個簡單的勻速運動框架 需求 接受物件、物件需要改變的屬性值、改變過程使用的時間等引數 實現 在一定的時間內,把物件指定的屬性,改變對應的變化量(這裡是變化量不是目標量),比如使用1s的事件讓div的height增加

Java Web定義MVC框架詳解

分享一個大神的人工智慧教程!http://blog.csdn.net/jiangjunshow 最近給學生講Java Web,希望他們能夠在學完這部分內容後自己實現一個MVC框架。但是突然發現百度上能搜尋到的靠譜的資料並不是很多,有些只是原理沒有程式碼實現,有些有程式碼實現但是

定義小型框架概述

造框架基礎 資料儲存的三種方式 xml解析 properties解析 註解解析 io流 動態代理 工廠模式 以上基礎掌握好之後,就可以準備造一個簡單的框

定義Web框架 定義Web框架

自定義Web框架 http協議 HTTP簡介 HTTP協議是Hyper Text Transfer Protocol(超文字傳輸協議)的縮寫,是用於從全球

【Android進階】如何寫一個很屌的動畫(1)---先實現一個簡易的定義動畫框架

class MyView extends View { public void onDraw(Canvas canvas) { super.onDraw(canvas); invalidate(); } } 這樣一來,View每次繪製都是觸發下一次繪製,不過

(二十四)定義動畫框架

一、效果 這邊程式碼很簡單,主要學習這個動畫框架開發過程的思想,開發出來的動畫框架便於使用,利於擴充套件。 二、分析 實現滾動是使用了 ScrollView,如果說要使用 ListView 的話,理論上也是可以的,但是 Item 型別比較多

定義View框架完全解析

前言        在Android中有很多的控制元件來供大家使用,但是和強大的IOS相比,Android所提供的控制元件平淡了許多,由於Android的開源可以讓每個開發者都能建立自己的檢視控制元件來滿足自己的需求,正因為這樣就出現各種各樣的自定義控制元件,久而久之就形成

手把手教你如何定義DAO框架(重量級乾貨)(yet)

https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247484864&idx=2&sn=9721e840eab2b929e9523d82c45a1bb6&chksm=ebd63aec

day48:django前戲之HTTP協議&定義web框架

目錄 1.HTTP協議   1.HTTP協議簡介   2.HTTP協議概述   3.HTTP協議工作原理   4.HTTP協議請求方法   5.HTTP協議狀態碼   6.URL   7.HTTP請求格式   8.HTTP響應格式 2.自定義web框架   1.第一版   2.第二版(函式版)   3.第三版

NancyFx 2.0的開源框架的使用-CustomModule(定義模塊)

nuget eat ews pub 技術分享 continue for eth color NancyFx框架的自定義模塊 新建一個空的Web項目 然後通過NuGet庫安裝下面的包 Nancy Nancy.Hosting.Aspnet 然後添

定義統一api返回json格式(app後臺框架搭建三)

pub ble ace proc 2.3 resp think err ons 在統一json自定義格式的方式有多種:1,[email protected]/* */,2,自定義一個註解,自己去解析對象成為json字符串進行返回 第一種方式,我就不推薦,想弄得的