Struts2開山篇【引入Struts、自定義MyStruts框架】

前言

這是Strtus的開山篇,主要是引入struts框架…爲何要引入struts,引入struts的好處是什麼….html

爲何要引入struts?

首先,在講解struts以前,咱們來看看咱們之前寫的Servlet,下面我就隨便截幾張圖來講明問題了…java

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

Servlet做爲MVC的Controller,無非就是三個步驟web

  • 獲得web層的數據、封裝到JavaBean
  • 調用Service的邏輯代碼
  • 跳轉到相對應的JSP頁面

當咱們寫Servlet的時候,通常都離不開這三個步驟,也能夠說,這是Servlet的「固定寫法」markdown

那咱們這樣寫代碼,有啥很差的地方呢??再從新看回上圖,咱們發現幾個弊端app

  • 咱們寫一個項目須要很是很是多的Servlet,這就形成十分冗餘..爲了職責分明,咱們卻不得不這麼作…
  • 跳轉到JSP頁面的路徑被咱們寫死了【一旦有別的需求,就須要改源代碼】

爲了解決上邊的弊端,struts就應運而生了框架


自定義struts

在正式講解struts以前,咱們來看一下,以咱們如今的水平,可以怎麼優化它。。jsp

以用戶的登錄註冊案例來進行說明ide

傳統的用戶登錄註冊

  • dao
public class UserDao {

    public User login(User user) {

        if ("aaa".equals(user.getUsername()) && "123".equals(user.getPsd())) {

            System.out.println("登錄成功!");
            return user;

        } else {
            System.out.println("登錄失敗!");
            return null;
        }
    }

    public void register(User user) {

        System.out.println("註冊成功!" + user.getUsername());
    }


}
  • service
public class UserService {

    private UserDao userDao = new UserDao();

    public User longin(User user) {
        return userDao.login(user);
    }

    public void register(User user) {
        userDao.register(user);
    }

}
  • loginServlet
@javax.servlet.annotation.WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //獲得用戶帶過來的數據,封裝到Bean對象中
        String username = request.getParameter("username");
        String psd = request.getParameter("psd");

        User user = new User();
        user.setPsd(psd);
        user.setUsername(username);

        try {
            //調用Service方法
            UserService userService = new UserService();
            userService.longin(user);

            //登錄成功跳轉到首頁
            request.getRequestDispatcher("/index.jsp").forward(request, response);

        } catch (Exception e) {
            e.printStackTrace();

            //登錄失敗,跳轉到相關的提示頁面
            request.setAttribute("message","登錄失敗了!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }

    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        this.doPost(request, response);

    }
}
  • registerServlet
@javax.servlet.annotation.WebServlet(name = "RegisterServlet",urlPatterns = "/RegisterServlet")
public class RegisterServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //獲得用戶帶過來的數據,封裝到Bean對象中
        String username = request.getParameter("username");
        String psd = request.getParameter("psd");

        User user = new User();
        user.setPsd(psd);
        user.setUsername(username);

        try {
            //調用Service方法
            UserService userService = new UserService();
            userService.register(user);

            //註冊成功跳轉到登錄界面
            request.getRequestDispatcher("/login.jsp").forward(request, response);

            //註冊成功,我也能夠跳轉到首頁
            //request.getRequestDispatcher("/index.jsp").forward(request, response);

        } catch (Exception e) {
            e.printStackTrace();

            //註冊失敗,跳轉到相關的提示頁面
            request.setAttribute("message","註冊失敗了!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }

    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        this.doPost(request, response);

    }
}
  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>


  <form action="${pageContext.request.contextPath}/LoginServlet" method="post">

    用戶名:<input type="text " name="username">
    密碼:<input type="password " name="psd">
    <input type="submit" value="登錄">
  </form>
  </body>
</html>
  • register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>


  <form action="${pageContext.request.contextPath}/RegisterServlet" method="post">

    用戶名:<input type="text " name="username">
    密碼:<input type="password " name="psd">
    <input type="submit" value="註冊">
  </form>
  </body>
</html>

上面的代碼已經通過了測試,是能夠跑起來的。post


①:跳轉頁面的路徑是寫死的。我在註冊成功了之後,我可以跳轉到首頁上,也能夠跳轉到登錄的界面上。若是我要選擇其中的一個,就必須修改源代碼測試

②:一個功能對應一個Servlet,太麻煩了…寫了LoginServlet,還要寫RegisterServlet….


新型的用戶登錄註冊

咱們會發現,不管什麼Servlet上最終仍是跳轉到相對應的JSP頁面的...也就是說,第一和第二步驟【封裝數據、調用Service】咱們能夠封裝起來…只要返回uri給Servlet跳轉到JSP頁面就行了


LoginAction

返回的uri分兩種狀況:

  • 若是是轉發,那麼返回的是RequestDispatcher對象
  • 若是是重定向,那麼返回的是字符串
/** * Created by ozc on 2017/4/26. * <p> * 一個Action對應一個Servlet,Action負責處理具體的請求 */
public class LoginAction {


    public Object login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        Object uri ;

        //獲得用戶帶過來的數據,封裝到Bean對象中
        String username = request.getParameter("username");
        String psd = request.getParameter("psd");

        User user = new User();
        user.setPsd(psd);
        user.setUsername(username);

        try {
            //調用Service方法
            UserService userService = new UserService();
            userService.longin(user);

            //登錄成功跳轉到首頁
            request.getSession().setAttribute("user", user);

            //跳轉到首頁的時候須要重定向
            //response.sendRedirect(request.getContextPath() + "/index.jsp");

            //若是是重定向,那麼返回的是字符串
            uri = "/index.jsp";
            return uri;

        } catch (Exception e) {
            e.printStackTrace();

            //登錄失敗,跳轉到相關的提示頁面
            request.setAttribute("message","登錄失敗了!!!");
            //request.getRequestDispatcher("/message.jsp").forward(request, response);

            //若是是轉發,那麼返回的是RequestDispatcher對象
            uri = request.getRequestDispatcher("/message.jsp");
            return uri;
        }
    }
}
  • LoginServlet就能夠寫成這樣了:
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //獲得LoginAction對象
        LoginAction loginAction = new LoginAction();
        Object uri = loginAction.login(request, response);


        //是重定向
        if (uri instanceof String) {
            response.sendRedirect(request.getContextPath() + uri);
        } else {

            //是轉發,強轉成是RequestDispatcher對象
            ((RequestDispatcher) uri).forward(request, response);
        }
    }

RegisterAction

  • RegisterAction
/** * Created by ozc on 2017/4/26. * * 一個Action對應一個Servlet,Action負責處理具體的請求 */ public class RegisterAction { public Object register(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { Object uri ; //獲得用戶帶過來的數據,封裝到Bean對象中 String username = request.getParameter("username"); String psd = request.getParameter("psd"); User user = new User(); user.setPsd(psd); user.setUsername(username); try { //調用Service方法 UserService userService = new UserService(); userService.register(user); //登錄成功跳轉到登錄頁面 uri = request.getRequestDispatcher("/login.jsp"); return uri; } catch (Exception e) { e.printStackTrace(); //註冊失敗,跳轉到相關的提示頁面 request.setAttribute("message","註冊失敗了!!!"); //request.getRequestDispatcher("/message.jsp").forward(request, response); uri = request.getRequestDispatcher("/message.jsp"); return uri; } } } 
  • RegisterServlet
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //獲得RegisterAction
        RegisterAction registerAction = new RegisterAction();

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

        //是重定向
        if (uri instanceof String) {
            response.sendRedirect(request.getContextPath() + uri);
        } else {

            //是轉發,強轉成是RequestDispatcher對象
            ((RequestDispatcher) uri).forward(request, response);
        }
    }

思考

到目前爲止,咱們搞了兩個Action類來封裝Servlet的邏輯代碼,咱們再次看回Servlet的代碼。

這裏寫圖片描述

這裏寫圖片描述

能夠很清楚地發現:兩個實現不一樣功能的Servlet僅僅是調用的Action不一樣….若是是僅僅調用的Action不一樣【經過反射來調用不一樣的Action】,那麼咱們應該想到使用一個Servlet來管理整個項目,也就是說:整個web項目只有一個核心的控制器

問題:

①:咱們在以前是直接指明Servlet的映射路徑了,如今要ActionServlet處理全部的請求,咱們只要定一個規則:只要後綴爲.action的,那麼都交由核心控制器ActionServlet來控制….

②:如今所有的請求已經交由ActionServlet控制,那怎麼知道調用的是哪一個Action???咱們能夠經過請求的uri,好比:http://localhost:8080/login.action,其中login就表明的是調用LoginAction..也就是說login=LoginAction,咱們能夠經過properties文件來配置..

③:如今咱們已經知道了調用的是哪一個Action了,可是Action可能不只僅只有一個方法,咱們還要在調用的時候,指定的方法名是什麼.這很簡單,通常咱們都是職責分工明確的,method=login….而且,調用的Action和具體的方法也是有關係的,不多是孤立存在的。所以,咱們的配置文件是不能使用properties的,須要使用XML

④:在調用方法的時候,是返回一個Object的uri的,uri的類型多是String、也能夠能是RequestDispatcher、而且返回的結果可能有幾種狀況的【可能跳轉到首頁,也可能跳轉到登錄界面】

⑤:Action調用的方法和返回的uri也是是有關係的!…..不一樣的Action調用不一樣的方法,返回的uri也是不一樣的….

⑥:要跳轉到哪一個頁面上,能夠經過標識量來識別….好比:success表示成功執行,若是要重定向那麼多加個type類型,若是不重定向就沒有type類型..路徑使用path來表示..所以,在具體的Action中,就不須要返回具體的uri,只要返回一個標識量便可


畫一張圖來梳理一下思路:

這裏寫圖片描述


XML配置

咱們能夠寫出這樣的XML配置,當ActionServlet初始化的時候,讀取XML配置文件,就知道調用的是什麼Action,Action中的什麼方法,以及跳轉到哪一個頁面上了

<?xml version="1.0" encoding="UTF-8" ?>
<mystruts>
    <package>

        <action name="login" className="zhongfucheng.servlet.LoginServlet" method="login">
            <!--是否存在type屬性,存在則是重定向,不存在則是轉發-->
            <!--result的值表示的就是跳轉的路徑-->
            <result name="success" type="redirect">/index.jsp</result>
            <result name="fail">/message.jsp</result>

        </action>
        <action name="register" className="zhongfucheng.servlet.RegisterServlet" method="register">
            <!--是否存在type屬性,存在則是重定向,不存在則是轉發-->
            <!--result的值表示的就是跳轉的路徑-->
            <result name="success">/message.jsp</result>
            <result name="fail">/message.jsp</result>
        </action>
    </package>

</mystruts>

爲了更好地管理這些信息,咱們應該使用JavaBean來對它們封裝

  • ActionMappingManager——-管理所有的Action
/** * Created by ozc on 2017/4/26. * * 該類管理着所有的Action * * 要管理所有的Action,就須要用一個容器來裝載這些Action * * 選擇Map集合是最合適的,能夠經過key來獲得Action,key就是<action name=><action/>中的name屬性 * */
public class ActionMappingManager {

    private Map<String, ActionMapping> map = new HashMap<>();

    //注意:外界都是經過name來獲得對應的Action的,並不會獲取獲得整個Manager
    public ActionMapping getActionMapping(String name) {
        return map.get(name);
    }

}
  • ActionMapping—-表示單個的Action
public class ActionMapping {

    //全部的results
    private Map<String, Results> results;

    //關鍵字name
    private String name;

    //要調用的Action路徑
    private String className;

    //Action中的方法
    private String method;

    public Map<String, Results> getResults() {
        return results;
    }

    public void setResults(Map<String, Results> results) {
        this.results = 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;
    }
}
  • Results—表示的是結果視圖
/** * Created by ozc on 2017/4/26. * * 該類表示的是結果視圖 * * * */ public class Results { //方法返回的標識 private String name; //要跳轉的方式 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; } } 

ActionMappingManager讀取配置文件

在ActionMappingManager中,應該讀取配置文件,而後把信息所有封裝到裏邊去…

/** * Created by ozc on 2017/4/26. * * 該類管理着所有的Action * * 要管理所有的Action,就須要用一個容器來裝載這些Action * * 選擇Map集合是最合適的,能夠經過key來獲得Action,key就是<action name=><action/>中的name屬性 * */
public class ActionMappingManager {
    private Map<String, ActionMapping> allAction ;

    public ActionMappingManager() {
        this.allAction = new HashMap<>();

        //讀取配置文件信息
        init();

    }


    public void init() {
        /********經過DOM4J讀取配置文件信息*********/

        try {
            //獲得解析器
            SAXReader saxReader = new SAXReader();

            //讀取在類目錄下的mystruts.xml文件
            InputStream stream = ActionMappingManager.class.getClassLoader().getResourceAsStream("mystruts.xml");

            //獲得表明XML文件的Document對象
            Document document = saxReader.read(stream);

            //經過XPATH直接獲得全部的Action節點
            List list = document.selectNodes("//action");

            //獲得每一個Action節點
            for (int i = 0; i < list.size(); i++) {
                Element action = (Element) list.get(i);

                //把獲得每一個Action的節點信息封裝到ActionMapping中
                ActionMapping actionMapping = new ActionMapping();

                String name = action.attributeValue("name");
                String method = action.attributeValue("method");
                String className = action.attributeValue("className");
                actionMapping.setName(name);
                actionMapping.setMethod(method);
                actionMapping.setClassName(className);

                //獲得action節點下的全部result節點
                List results = action.elements("result");

                //獲得每個result節點
                for (int j = 0; j < results.size(); j++) {
                    Element result = (Element) results.get(j);

                    //把獲得每一個result節點的信息封裝到Results中
                    Results results1 = new Results();

                    //獲得節點的信息
                    String name1 = result.attributeValue("name");
                    String type = result.attributeValue("type");
                    String page = result.getText();

                    results1.setName(name1);
                    results1.setType(type);
                    results1.setPage(page);

                    //把result節點添加到ActionMapping的集合中
                    actionMapping.getResults().put(name1, results1);
                }

                //最後把獲得每一個ActionMapping的信息添加到ActionMappingManager中
                allAction.put(name, actionMapping);

            }



        } catch (DocumentException e) {

            new RuntimeException("初始化的時候出錯了!「" + e);
        }
    }

    //注意:外界都是經過name來獲得對應的Action的,並不會獲取獲得整個Manager
    public ActionMapping getActionMapping(String name) {
        return allAction.get(name);
    }
}

ActionServlet

使用init()方法只加載建立一個ActionManagerMapping對象,並設置在Web容器啓動了該Servlet就啓動

/** * Created by ozc on 2017/4/26. * * * Web容器一啓動的時候,該類就應該加載了,在web.xml文件中配置onloadStart */

public class ActionServlet extends HttpServlet {


    //該對象封裝了全部的XML信息
    ActionMappingManager actionMappingManager ;
    @Override
    public void init() throws ServletException {

        //讓ActionMappingManager對象只有一個!
        actionMappingManager = new ActionMappingManager();
    }

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

        try {
            //獲得用戶的uri
            String uri = request.getRequestURI();

            //截取uri的關鍵部分-----截完應該是login
            uri = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));

            //經過uri獲得配置文件中的action信息
            ActionMapping actionMapping = actionMappingManager.getActionMapping(uri);

            //獲得action的類名,方法名
            String className = actionMapping.getClassName();
            String method = actionMapping.getMethod();

            //經過反射建立出Action的對象,調用對應的方法
            Class t = Class.forName(className);
            Object o = t.newInstance();

            //注意:這裏的參數是接口的class,不是單純的request的class,單純的class是實現類
            Method m = t.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);

            //調用方法,獲得標記
            String returnFlag = (String) m.invoke(o, request, response);


            //經過標記獲得result的具體信息
            Results result = actionMapping.getResults().get(returnFlag);
            String type = result.getType();
            String page = result.getPage();

            //判斷是重定向仍是轉發,爲空就是轉發,反則是重定向
            if (type == null) {
                response.sendRedirect(page);
            } else {
                request.getRequestDispatcher(request.getContextPath() + page).forward(request, response);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

        this.doPost(request, response);

    }
}

具體的Action的方法只要返回一個標識量便可,咱們經過標識量來獲得具體的跳轉頁面url和跳轉的方法的。。。


效果:

這裏寫圖片描述


總結:

因爲傳統web的Controller模塊存在弊端:

  • 須要建立很是多的Servlet
  • 跳轉的頁面寫死了。改變需求的時候須要更改源代碼

所以struts就應運而生了,本博文主要模擬Struts的開發流程

使用一個ActionServlet核心控制器來管理所有的Web請求,寫XML配置文件,讀取配置文件。經過uri來判斷要調用具體的Action,Action中的方法。獲得返回值,再根據XML文件的配置信息來肯定跳轉方法、跳轉的url

相關文章
相關標籤/搜索