詳解Java的Struts框架以及相關的MVC設計理念
struts簡介
Struts是Apache軟件基金會(ASF)贊助的一個開源項目。它最初是jakarta項目中的一個子項目,并在2004年3月成為ASF的頂級項目。它通過采用JavaServlet/JSP技術,實現(xiàn)了基于JavaEEWeb應用的MVC設計模式的應用框架,是MVC經(jīng)典設計模式中的一個經(jīng)典產(chǎn)品。
Struts發(fā)展歷史
Struts是作為ApacheJakarta項目的組成部分,項目的創(chuàng)立者希望通過對該項目的研究,改進和提高JavaServerPages、servlet、標簽庫以及面向?qū)ο蟮募夹g水準。
Struts這個名字的來源于在建筑和舊式飛機中使用的支持金屬架。之所以這個框架叫做“struts”,是為了提醒我們記住那些支撐我們房屋,建筑,橋梁,甚至我們踩高蹺的基礎支撐。這也是解釋struts在開發(fā)web應用程序中所扮演的角色的精彩描述。
Struts的含義是”支柱,枝干”,它的目的是為了減少程序開發(fā)的時間,項目的創(chuàng)建者認為JSP,servlet的存在雖然可以幫助用戶解決大部分問題,但是由于它們的編碼對項目的開發(fā)帶來了許多的不方便,可重用性也差,所以struts應運而生,幫助用戶在最短的時間內(nèi)解決這些問題。Struts框架提供如下服務:
(1)作為控制器的Servlet。
(2)提供大量的標簽庫。
(3)提供了用于國際化的框架,利用不同的配置文件,可以幫助用戶選擇合適自己的語言。
(4)提供了JDBC的實現(xiàn),來定義數(shù)據(jù)源和數(shù)據(jù)庫連接池。
(5)XML語法分析工具。
(6)文件下載機制。
Struts原理
Struts是對JSPModel2設計標準的一種實現(xiàn),下面分別從模型(Model)、視圖(view)和控制器3個部分介紹Struts的體系結構和工作原理。調(diào)用流程如下所示。
(1)視圖(view)
在Struts中,視圖層包含兩個部分,JSP頁面和ActionForm。
ActionForm封裝了用戶提交的表單信息,其實ActonForm本質(zhì)上就是JavaBean,這些JavaBean中沒有業(yè)務邏輯,只提供了所有屬性的getter和setter方法,這些屬性和用戶表單中的輸入項是一一對應的。在Struts中就是通過ActionForm把用戶表單信息提交給控制器。
JSP頁面是經(jīng)典MVC中主要的視圖組件,主要是信息顯示和控制器處理結果顯示的功能。
除了以上,struts還提供了一個強大的struts標簽庫,來幫助用戶解決顯示邏輯,并且利用ActonForm組件將信息傳遞到控制層。
(2)控制器(Controller)
在控制層,struts提供了一個控制器組件ActionServlet,它繼承自HttpServlet,并重載了HttpServlet的doGet(),doPost()方法,可以接受HTTP的響應,并進行轉(zhuǎn)發(fā),同時還提供了使用XML進行轉(zhuǎn)發(fā)Mapping(映射)的功能。
(3)模型(Model)
模型表示狀態(tài)和業(yè)務邏輯的處理,在一般的web應用程序中,用JavaBean或者EJB來實現(xiàn)系統(tǒng)的業(yè)務邏輯。在Struts中,struts提供Action對象,來管理業(yè)務邏輯的調(diào)用,幫助用戶分離業(yè)務邏輯,也就是說struts本身不實現(xiàn)業(yè)務邏輯,但可以調(diào)用已完成的業(yè)務邏輯。
Struts工作流程
Struts工作流程如下所示。
ActionServlet是struts中核心的控制器,所有的用戶請求都必須通過ActionServlet的處理,而struts-config.xml是struts中核心的配置文件,在這個文件中配置了用戶請求URL和控制器Action的映射關系,ActionServlet通過這個配置文件把用戶的請求發(fā)送到對應的控制器中。
在struts web應用程序中,當web應用程序啟動的時候,就會初始化ActionServlet在初始化ActionServlet的時候會加載struts-config.xml配置文件,在加載成功后會把這些URL和控制器映射關系存放在ActionMapping對象或者其他對象中。當ActionServlet接收到用戶請求的時候,就會按照下面的流程對用戶請求進行處理。
(1)ActionServlet接收到用戶的請求后,會根據(jù)請求URL尋找匹配的ActionMapping對象,如果匹配失敗,說明用戶請求的URL路徑信息有誤,所以返回請求路徑無效的信息,當找到匹配的ActionMapping的時候,進入到下一步。
(2)當ActionServlet找到匹配的ActionMapping對象的時候,會根據(jù)ActionMapping中的映射信息判斷對應的ActionForm對象是否存在,如果不存在對應的ActionForm對象就創(chuàng)建一個新的ActionForm對應,并把用戶提交的表單信息保存到這個ActionForm對象中。
(3)在struts-config.xml中這個配置文件,可以配置表單是否需要驗證,如果需要驗證,就調(diào)用ActionForm中的validate()方法對用戶輸入的表單進行驗證。
(4)如果ActionForm的validate()方法返回了ActionErrors對象,則表明驗證失敗,ActionServlet把這個頁面返回到用戶輸入的界面,提示用戶重新輸入。如果方法的返回值為null,就表明驗證已經(jīng)通過,可以進入下一步處理。
(5)ActionServlet可以根據(jù)ActionMapping對象查找用戶請求轉(zhuǎn)發(fā)給哪個控制器Action,如果對應的Action對象不存在,就創(chuàng)建這個對象,并調(diào)用這個Action的excute()方法。
(6)業(yè)務邏輯控制器Action的execute()方法就會返回一個ActionForward對象,ActionServlet把控制器處理的結果轉(zhuǎn)發(fā)到ActionForward對象指定的JSP頁面。
(7)ActionForward對象指定的JSP頁面根據(jù)返回的處理結果,用合適形式把服務器處理的結果展示給用戶,到這里為止,一個客戶請求的整個過程完畢。
以上初步struts框架進行了介紹,和對原理進行了簡單的分析。至于struts是如何實現(xiàn)MVC的,ActionServlet屬于Controller部分,Action和ActionForm屬于Model層,還是Action屬于Controller層,不同的人對struts有不同的理解。接下來真正的運用到實踐中,在實踐中深刻去體會,原理固然重要,重要的是運用,是能駕馭和使用這個框架。就像學習開車一樣,不是一蹴而就的。
MVC向struts MVC框架演變過程
版本一 基本的MVC
首先是創(chuàng)建一個jsp索引頁面index.jsp,index.jsp創(chuàng)建一個表單。
如果我們完成添加用戶的模塊,我們提交表單的時候要把這個表單提交給一個servlet,于是我們在src自己的包中建立自己的servlet繼承HttpServlet,TestServlet同時覆蓋父類的doGet和doPost方法。
提交的的表單信息,我們可以在TestServlet中通過request.getParameter(“username”);取出來。之后把從表單中的數(shù)據(jù)提交到數(shù)據(jù)庫,我們的servlet作為控制器沒有添加到數(shù)據(jù)庫的職責,于是我們把訪問數(shù)據(jù)庫的業(yè)務邏輯放到UserManager.java類中。在這個類中實現(xiàn)添加用戶的任務。
package com.bjpowernode.servlet; public class UserManager { public void add(String username) { System.out.println("UserManager.add()-->>"+ username); } }
在TestServlet中調(diào)用UserManager中的方法。同時讓頁面轉(zhuǎn)向到添加成功的頁面add_success.jsp。
TestServlet代碼以及在web.xml中配置TestServlet.
<servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>com.bjpowernode.servlet.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/servlet/TestServlet</url-pattern> </servlet-mapping>
Testservlet代碼如下所示:
package com.bjpowernode.servlet; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; importjavax.servlet.http.HttpServlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class TestServlet extendsHttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter(“username”); UserManager userManager = new UserManager(); userManager.add(username); request.getRequestDispatcher(“/add_success.jsp”).forward(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
索引頁面index.jsp代碼,通過servlet/TestServlet來轉(zhuǎn)到TestServlet。
<form action="servlet/queryUser.action" method="post"> 姓名:<input type="text" name="username"><br> <input type="submit" value="提交"><br> </form>
這樣采用MVC完成了用戶的添加任務。
版本二 通過判斷標識變量
當我們不僅要完成用戶的添加而是要完成用戶的增刪改查的時候我們可以在servlet中進行判斷,是添加還是刪除等。并且在傳遞字符串的時候要加上一個標識表示相應的操作。這樣在servlet中會有很多的if和else語句。
版本三 servlet模式匹配 截取字符串判斷
我們可以在配置servlet的時候配置成為匹配模式<url-pattern>*.action</url-pattern>任何的*.action的都會轉(zhuǎn)到到TestServlet,這樣我們可以截取URL來判斷轉(zhuǎn)到哪個頁面。但在TestServlet中仍然有很多的ifelse語句進行判斷。這樣我們的TestServlet代碼如下所示。
import java.io.IOException; import java.util.List; importjavax.servlet.ServletException; importjavax.servlet.http.HttpServlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class TestServlet extendsHttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //截取url. String requestURI = request.getRequestURI(); //System.out.println("requestURI="+ requestURI); //截取http://localhost:8080/test_servlet/servlet/addUser.action test_servlet后面的東西。 String path = requestURI.substring(requestURI.indexOf("/", 1),requestURI.indexOf(".")); //截取得到虛目錄。/servlet/addUser System.out.println("path="+ path); String username = request.getParameter("username"); UserManager userManager = new UserManager(); String forward = ""; //判斷截取的path和哪個要加載的頁面相等. if("/servlet/delUser".equals(path)) { userManager.del(username); forward = "del_success.jsp"; //request.getRequestDispatcher("/del_success.jsp").forward(request,response); }else if("/servlet/addUser".equals(path)) { userManager.add(username); forward= "add_success.jsp"; //request.getRequestDispatcher("/add_success.jsp").forward(request,response); }else if("/servlet/modifyUser".equals(path)) { userManager.modify(username); forward= "modify_success.jsp"; //request.getRequestDispatcher("/modify_success.jsp").forward(request,response); }else if("/servlet/queryUser".equals(path)) { List userList = userManager.query(username); request.setAttribute("userList",userList); forward= "query_success.jsp"; //request.getRequestDispatcher("/query_success.jsp").forward(request,response); }else { throw new RuntimeException("請求失敗!"); } request.getRequestDispatcher(forward).forward(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException { doGet(request,response); } }
UserManager代碼如下所示:
package com.bjpowernode.servlet; import java.util.ArrayList; import java.util.List; public class UserManager { public void add(String username) { System.out.println("UserManager.add()-->>"+ username); } public void del(String username) { System.out.println("UserManager.del()-->>"+ username); } public void modify(String username) { System.out.println("UserManager.modify()-->>"+ username); } public List query(String username) { System.out.println("UserManager.query()-->>"+ username); List userList = new ArrayList(); userList.add("a"); userList.add("b"); userList.add("c"); return userList; } }
同時建立查詢成功,刪除成功,修改成功jsp頁面。
這樣在index.jsp頁面中通過表單的action屬性來轉(zhuǎn)到相應的頁面。
<body> <form action="servlet/queryUser.action" method="post"> 姓名:<input type="text" name="username"><br> <input type="submit" value="提交"><br> </form> </body>
版本三的缺點是if太多,不穩(wěn)定,當我們添加或者刪除一個if的時候還需要重新編譯源程序。這樣無法適應變化的需求。所以我們在此基礎上進行改進就需要去掉if語句,可以把改變的部分分離出來,變成可配置的,這樣就變得靈活的,需要改動jsp的文件名,在配置文件中配置一下就可以了。
版本四 if else抽象出接口和類+配置文件
首先我們把轉(zhuǎn)到的地址字符串放到一個字符串變量中,這樣TestServlet中的doGet方法,代碼如下。
String username = request.getParameter("username"); UserManager userManager = new UserManager(); String forward = ""; //判斷截取的path和哪個要加載的頁面相等. if("/servlet/delUser".equals(path)) { userManager.del(username); forward= "del_success.jsp"; }else if("/servlet/addUser".equals(path)) { userManager.add(username); forward= "add_success.jsp"; }elseif("/servlet/modifyUser".equals(path)) { userManager.modify(username); forward= "modify_success.jsp"; }else if("/servlet/queryUser".equals(path)) { List userList = userManager.query(username); request.setAttribute("userList",userList); forward= "query_success.jsp"; }else { thrownew RuntimeException("請求失敗!"); } request.getRequestDispatcher(forward).forward(request,response);
統(tǒng)一完成了轉(zhuǎn)向。當if語句是滿足某種條件,條件不同的時候轉(zhuǎn)到不同的頁面,添加有添加的邏輯,修改有修改的邏輯,這樣我們可以抽象出接口。對添加的action操作添加,對刪除的action做出刪除任務,對修改的action做出修改。把每一個if語句中的變成為類去實現(xiàn),抽象出一個Action接口,接口中方法execute()方法,返回轉(zhuǎn)向路徑字符串。類圖如下所示。
共同的Action接口,不同的實現(xiàn)。
Action接口代碼。
package com.bjpowernode.servlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public interface Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception; }
AddUserAction代碼。
package com.bjpowernode.servlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class AddUserActionimplements Action { @Override public String execute(HttpServletRequest request,HttpServletResponse response) throws Exception { String username = request.getParameter("username"); //Stringsex = request.getParameter("sex"); //........... //調(diào)用業(yè)務邏輯. UserManager userManager = new UserManager(); userManager.add(username); return"/add_success.jsp"; } }
DelUserAction代碼。
package com.bjpowernode.servlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class DelUserActionimplements Action { @Override public String execute(HttpServletRequest request,HttpServletResponse response) throws Exception { String username = request.getParameter("username"); //String sex = request.getParameter("sex"); //........... //調(diào)用業(yè)務邏輯. UserManager userManager = new UserManager(); try { userManager.del(username); }catch(Exceptione) { return"del_error.jsp"; } return"/del_success.jsp"; } }
ModifyUserAction代碼。
package com.bjpowernode.servlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class ModifyUserActionimplements Action { @Override public String execute(HttpServletRequest request,HttpServletResponse response) throwsException { String username = request.getParameter("username"); //String sex = request.getParameter("userId"); //...........等其他... //調(diào)用業(yè)務邏輯. UserManager userManager = new UserManager(); userManager.modify(username); return"/modify_success.jsp"; } }
QueryUserAction代碼。
package com.bjpowernode.servlet; import java.util.List; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; public class QueryUserActionimplements Action { @Override public String execute(HttpServletRequest request,HttpServletResponse response) throwsException { String username = request.getParameter("username"); //Stringsex = request.getParameter("userId"); //其他查詢條件. //...........等其他... //調(diào)用業(yè)務邏輯. UserManager userManager = new UserManager(); List userList = userManager.query(username); request.setAttribute("userList",userList); return"/query_success.jsp";//轉(zhuǎn)向路徑都可以通過配置文件讀取。 } }
這樣使用多態(tài)方式調(diào)用不同的Action,轉(zhuǎn)向代碼如下所示。
//用多態(tài)的方式. Action action = null; if("/servlet/delUser".equals(path)) { action= new DelUserAction(); }else if("/servlet/addUser".equals(path)) { action= new AddUserAction(); }else if("/servlet/modifyUser".equals(path)) { action= new ModifyUserAction(); }else if("/servlet/queryUser".equals(path)) { action= new QueryUserAction(); }else { throw new RuntimeException("請求失敗!"); } //取得action后傳遞過去。動態(tài)調(diào)用ACtion中的execute方法。 String forward = null; try{ forward= action.execute(request,response); }catch (Exception e) { e.printStackTrace(); } //根據(jù)路徑完成轉(zhuǎn)向。 request.getRequestDispatcher(forward).forward(request, response);
上述調(diào)用不同的action,我們可以把不同的請求和對應的action配置到自己的xml文件中。配置哪個請求對應哪個Action。
<action-config> <action path="/servlet/delUser" type="com.bjpowernode.servlet.DelUserAction"> <forward name="success">/del_success.jsp</forward> <forward name="error">/del_error.jsp</forward> </action> <action path="/servlet/addUser" type="com.bjpowernode.servlet.AddUserAction"> <forward name="success">/add_success.jsp</forward> <forward name="error">/add_error.jsp</forward> </action> <action path="/servlet/modifyUser" type="com.bjpowernode.servlet.ModifyUserAction"> <forward name="success">/modify_success.jsp</forward> <forward name="error">/modify_error.jsp</forward> </action> <action path="/servlet/queryUser" type="com.bjpowernode.servlet.QueryUserAction"> <forward name="success">/query_success.jsp</forward> <forward name="error">/query_error.jsp</forward> </action> </action-config>
這樣我們就可以讀取配置文件來進行相應的操作。每個action對應著一些信息,它的class,執(zhí)行成功以及執(zhí)行失敗的配置。我們在讀取配置文件的時候可以把這些放到Map對象中。代碼如下所示。
ActionMapping { private String path; private String type; Map forwardMap; } forwardMap { key="success"; value="/del_success.jsp" key="error"; value="/del_error.jsp"; } Map map = new HashMap(); map.put("/servlet/delUser",); map.put("/servlet/addUser",); map.put("/servlet/modifyUser",); map.put("/servlet/queryUser",); //如果是刪除的ActionMapping存儲如下: actionMapping { path="/servlet/delUser"; type="com.bjpowernode.servlet.DelUserAction"; forwardMap { key="success",value="/del_success.jsp"; key="error",value="/del_error.jsp"; } } String path ="/servlet/delUser"; //根據(jù)截取的URL請求,到Map中取得本次請求對應的Action。 ActionMapping actionMapping =(ActionMappint)map.get(path); // 取得本次請求對應的Action類的完成路徑。 String type =actionMappint.getType();//com.bjpowernode.servlet.DelUserAction //通過反射動態(tài)實例化Action Aciton action =(Action)class.forName(type).newInstance(); //取得action后傳遞過去。動態(tài)調(diào)用ACtion中的execute方法。 String forward =action.execute(request,response); //根據(jù)路徑完成轉(zhuǎn)向。 request.getRequestDispatcher(forward).forward(request,response);
我們用時序圖來描述上述調(diào)用過程(如圖)。
Struts就是采用上述思路。Struts框架是把上圖中前端控制器servlet進行了封裝,我們只要繼承struts的Action去調(diào)用業(yè)務邏輯和轉(zhuǎn)向。讀取配置文件的工作也是封裝到了struts框架中。前篇講解了struts框架的應用實例,我們可以進行對比。
相關文章
Java GZip 基于內(nèi)存實現(xiàn)壓縮和解壓的方法
這篇文章主要介紹了Java GZip 基于內(nèi)存實現(xiàn)壓縮和解壓的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08druid多數(shù)據(jù)源配置+Datasurce動態(tài)切換方式
這篇文章主要介紹了druid多數(shù)據(jù)源配置+Datasurce動態(tài)切換方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Mybatis之@ResultMap,@Results,@Result注解的使用
這篇文章主要介紹了Mybatis之@ResultMap,@Results,@Result注解的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

全面解析Java支持的數(shù)據(jù)類型及Java的常量和變量類型

java數(shù)據(jù)結構和算法中數(shù)組的簡單入門