雖然目前Struts MVC框架不怎麼用了,但它確是個能幫助大家很好地入門Web MVC框架,而且,一些歷史項目可能還用Struts,反正技多不壓身,大家如果能在面試中通過項目證明自己Struts這塊也很熟,這也是個非常好的加分項。 這裡我們就從搭建Struts基本框架入手,再深入講解些面... ...
雖然目前Struts MVC框架不怎麼用了,但它確是個能幫助大家很好地入門Web MVC框架,而且,一些歷史項目可能還用Struts,反正技多不壓身,大家如果能在面試中通過項目證明自己Struts這塊也很熟,這也是個非常好的加分項。
這裡我們就從搭建Struts基本框架入手,再深入講解些面試中常會靠到的Struts知識點,本文的文字和案例根據java web輕量級開發麵試教程改編。
///////////////////////////////////////////////////////////////////////////////////////
1 搭建Struts框架
1.1 開發前端的JSP代碼
創建一個Java Web項目,名為strutsDemo,在其中的WebRoot目錄下,創建前端的JSP代碼calSum.jsp。
1 1 <%@ page language="java" pageEncoding="GBK" %> 2 2 <%@ taglib prefix="s" uri="/struts-tags"%> 3 3 <html> 4 4 <head> 5 5 <title>輸入操作數</title> 6 6 </head> 7 7 <body> 8 8 求和<br/> 9 9 <s:form action="mystruts/calSum" > 10 10 <s:textfield name="num1" label="數1"/> 11 11 <s:textfield name="num2" label="數2" /> 12 12 <s:submit value="求和" /> 13 13 </s:form> 14 14 </body> 15 15 </html>
這個頁面的效果下圖所示。
在第2行里引入了Struts的標簽,首碼是s,所以可以用s:form和s:textfield來定義form和文本框。
在第9行到第13行的form里,定義了兩個輸入框和一個提交按鈕。當用戶輸入兩個數字後,單擊“求和”按鈕後,本頁面將根據定義在第9行的定義,跳轉到mystruts/calSum.action。
1.2 在web.xml里聲明使用Struts
如果要使用基於Struts的MVC,則必須要在web.xml里聲明,否則系統伺服器是不會知道在項目里用到了基於Struts的MVC處理器。web.xml的代碼如下。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app 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" version="2.5"> 3 <filter> 4 <filter-name>struts2</filter-name> 5 <filter-class> 6 org.apache.struts2.dispatcher.FilterDispatcher 7 </filter-class> 8 </filter> 9 <filter-mapping> 10 <filter-name>struts2</filter-name> 11 <url-pattern>/*</url-pattern> 12 </filter-mapping> 13 </web-app>
從第2行到第13行之間的web-app元素里,我們聲明瞭兩件事:
(1)通過第11行的url-pattern,說明/*,也就是任何請求,都將由名為struts2的過濾器來處理。
(2)在第3行到第8行之間,指定了struts2這個過濾器它的後臺處理類。
綜合上述兩點可知,本項目的任何請求,都將由Struts的後臺處理類來處理。
1.3 配置struts.xml文件
在calSum.jsp里指定了form跳轉的目的地。
<s:form action="mystruts/calSum" >
那麼這個目的地究竟是哪裡?一起來看一下struts.xml這個配置文件。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> 3 <struts> 4 <package name="struts2" namespace="/mystruts" 5 extends="struts-default"> 6 <action name="calSum" class="action.myAction"> 7 <result name="Positive">/positive.jsp</result> 8 <result name="Negative">/negative.jsp</result> 9 </action> 10 </package> 11 </struts>
在第4和第5行里,能看到名為calSum的action處於/mystruts這個命名空間里,它所對應的是action.myAction這個處理類。也就是說,calSum.jsp的請求最終是由action.myAction接收和處理。
1.4 開發Action類
隨後開發myAction.java,它是放在action這個package下的。
1 package action; 2 import com.opensymphony.xwork2.ActionSupport; 3 public class myAction extends ActionSupport 4 { 5 private int num1; 6 private int num2; 7 8 public String execute() throws Exception 9 { 10 // 如果和大於等於0,則跳到positive.jsp頁面 11 if (getSum() >= 0) 12 { 13 return "Positive"; 14 } 15 //否則跳到negative.jsp頁面 16 else 17 { 18 return "Negative"; 19 } 20 } 21 public int getNum1() { 22 return num1; 23 } 24 public void setNum1(int num1) { 25 this.num1 = num1; 26 } 27 public int getNum2() { 28 return num2; 29 } 30 public void setNum2(int num2) { 31 this.num2 = num2; 32 } 33 public int getSum() 34 { 35 return num1 + num2; // 計算兩個整數的代碼數和 36 } 37 }
作為一個Struts的Action,本類繼承了ActionSupport類。當一個請求最終到達本Action時,如果沒有額外的配置,就會如同本類一樣,由execute方法來處理請求。
在第8行的execute方法里可能看到,如果兩個數的和大於0,則返回Positive字元串,反之則返回Negative。這兩個返回是和struts.xml里的配置相對應的。
在struts.xml的第6行和第7行里,看到有如下的配置。
<result name="Positive">/positive.jsp</result>
<result name="Negative">/negative.jsp</result>
這說明根據不同的字元串,Struts處理容器將會跳轉到兩個不同的jsp里。在這個Action代碼里,並沒有給num1和num2賦值,這是因為它們和calSum.jsp里form中的兩個輸入框同名,所以會自動拿到我們輸入的值。
1.5 開發兩個跳轉結果頁面
上文提到過,Action的execute方法里返回的不同字元串後,Struts MVC處理器會讀取struts.xml里的配置,並相應地,跳轉到不同的頁面。下麵就來編寫這兩個頁面的代碼。先來看一下positive.jsp。
1 <%@ page language="java" pageEncoding="GBK"%> 2 <%@ taglib prefix="s" uri="/struts-tags" %> 3 <html> 4 <head> 5 <title>顯示和</title> 6 </head> 7 <body> 8 結果大於或等於0,<h1><s:property value="sum" /></h1> 9 </body> 10 </html>
這裡的關鍵代碼是在第8行,通過一個Struts的標簽,來獲取在Action里名為”sum”的屬性對象。在myAction.java里,定義了一個getSum的方法,所以就會自動生成一個sum的屬性。結果是兩個操作數之和。
Negative.jsp代碼和positive.jsp很相似。唯一的差別在第8行,這裡的敘述文字是“結果小於0”。
1 <%@ page language="java" pageEncoding="GBK"%> 2 <%@ taglib prefix="s" uri="/struts-tags" %> 3 <html> 4 <head> 5 <title>顯示和</title> 6 </head> 7 <body> 8 結果小於0,<h1><s:property value="sum" /></h1> 9 </body> 10 </html>
2 通過運行,瞭解Struts的工作流程
現在來總結一下整個程式的運行和跳轉流程。
第一,打開Tomcat伺服器,併在瀏覽器里輸入http://localhost:8080/strutsDemo/calSum.jsp,能看到如下圖所示的頁面
當輸入兩個數字,並單擊“求和”按鈕後,根據如下form里的定義,會跳轉到mystruts/calSum里。
<s:form action="mystruts/calSum" >
第二,根據web.xml的定義,可知這個跳轉請求將由struts來處理。再根據struts.xml的定義,得知這個請求最終將由myAction.java來處理。
<action name="calSum" class="action.myAction">
<result name="Positive">/positive.jsp</result>
<result name="Negative">/negative.jsp</result>
</action>
第三,在myAction.java的execute方法里,根據最終sum的值,分別返回兩個不同的字元串,從上文struts.xml片段的第3行和第4行得知,根據兩個不同的返回值,會跳轉到兩個不同的頁面里。
3 和JSP+Servlet+JavaBean框架的比較
在一個項目里,我們應更關註“業務該怎麼處理”這個問題,而不應把大多數精力放在調試JSP到Servlet之類的跳轉上。
Struts給我們提供了一套跳轉機制,我們可以簡單地通過編寫struts.xml和web.xml,就能比較省心地實現從前端JSP跳轉到具體業務處理類的功能。而且,也只需在Action類里編寫返回字元串,同時在struts.xml里編寫返回字元串和跳轉頁面的對應關係,就能根據業務執行結果方便地跳轉回前端頁面。
Struts框架是基於MVC的,它的View部分主要由JSP頁面實現。Controler部分主要由Action類來承擔,而Model部分主要由ActionForm組成。需要說明的是,在Struts2.0以上的版本里,開發者不需要額外定義ActionForm,由用戶在前端Form里傳入的數據將被自動封裝成ActionFrom對象,並被最終轉發給Action。
通過下表對比一下Struts和前文提到的JSP+Servlet+JavaBean框架,綜合各項對比的指標,Struts略優於JSP+Servlet+JavaBean框架。
比較項 |
Struts |
JSP+Servlet+JavaBean |
結論 |
如何在後端接收前端傳來的參數 |
參數組裝成ActionForm,並自動發送到Action里 |
需要在Servlet里編寫接收參數的代碼 |
Struts比較省心 |
如何把前端的請求發送到合適的處理頁面 |
可以在struts.xml里統一定義請求和處理類的對應關係 |
可以在web.xml里統一定義請求和處理類的對應關係 |
基本持平 |
後端處理請求的方式 |
定義在Action里 |
大多定義在Servlet類的doPost或doGet方法里 |
基本持平 |
如何把後端的處理結果再回傳到前端 |
可以在struts.xml里統一地定義處理結果和返回頁面的對應關係 |
需要在Servlet里手動地跳轉 |
Struts略優 |
項目的開發方式 |
程式員的工作量比較少,在必要的地方(比如Action類和Struts.xml)里填寫必要的代碼即可,Struts處理器能方便地實現MVC之間的跳轉 |
程式員可能得操心必要的細節,比如Servlet里如何接收參數,如何跳轉到前端,等等 |
Struts的開發流程比較省心 |
4 對Struts框架的進一步瞭解
Struts作為一個基於MVC的框架,能很好地處理跳轉業務,能讓程式員把精力更多地集中到業務開發上,通過它,程式員能很好地瞭解框架編程的思路和一般方法。
不過任何框架都不是十全十美的,當你比較熟悉Struts框架後,一定能感受到它在項目開發里的一些缺陷。對於Struts的局限性,不同的人有不同的觀點。但是請大家記住,在面試時,當你結合你的項目自信地說出Struts框架的局限時,即使面試官和你的觀點不一致,他也一定會認為你對Struts有足夠的認識。這裡羅列出一些局限性供大家參考,如表所示。
局限性 |
結合項目說明 |
為每個請求創建一個Action |
Struts2里,會為每個http請求實例化一個Action 對象,這對處理高併發會有些難度 |
對Action 執行前和後的處理支持不大好 |
比如每次進到Action前我們需要列印記憶體使用量,執行後需要記錄跳轉目標URL,這個當然可以直接寫到Action里,但大家比較下Spring的AOP處理方式,就會發現單純把前後處理寫到Action里有什麼不足 |
對跳轉的支持 |
如果在一個Action里,根據處理結果可能會跳轉到10個頁面,那麼代碼可能會比較煩瑣。而且一旦跳轉目標頁面出現變更,比如換了目錄,那麼在修改配置文件後,可能要求重新部署和重啟Web伺服器,無法實現輕量級修改 |
安全性上的瑕疵 |
我們可以通過類似方式直接執行Action里的方法:http://url:8080/proName/userLogin!list 但事實上,該系統需要用戶先登錄才能開放用戶列表。雖然我們可以通過定義攔截器來彌補這個缺陷,但畢竟屬於額外的工作量 |
屬於侵入式 |
在使用Struts時,需要繼承struts的類,而且要編寫execute方法。這樣Struts框架就侵入到項目里了。比如做的是銀行業務,如果哪天要把這套業務移植到其他Spring等框架項目里,可能工作量就比較大了 |
5 關於Struts面試點的歸納
Struts的地位似乎有些尷尬,一些小型(比如10個頁面左右)的商業項目可能直接會用JSP+Servlet+JavaBean+DB的開發模式,一些大型基於企業級的項目往往採用Spring+MyBatis的框架。
我們在面試初級程式員時,如果候選人沒有在商業項目里用過Struts框架,這很正常,如果他在學校或者培訓機構學過Struts,這或許也可以成為一個加分項。但如果我們看到某人在最近的商業項目里用過,那麼就會詳細問原因,為什麼這個公司現在還要用Struts框架?反而有不少人,在這個問題上會弄巧成拙。我們聽到的最多的合理回答是,基於某種原因,比如歷史原因或者客戶要求,這個項目還是得用Struts框架。
具體到面試題,大家可以從網上找,在必要的時候,大家也可以多多益善地突擊準備。我們給大家推薦回答問題的方式是“結合項目”,請大家看如下的問題點。
第一,說明在這個項目里,都用到了Struts的哪些組件,比如攔截器或者驗證器,你是怎麼用的?
第二,結合項目,說明自己做了哪些工作,比如在Action里,你怎麼和業務以及資料庫代碼耦合?
第三,能結合項目,告訴考官一些技術細節,比如攔截器是怎麼用的,在該項目里結合一個需求,告訴考官攔截器的工作流程和開發方式。或者結合項目說明下驗證器的用法。
也就是說,任何技術都別停留在紙上,需要結合項目說明。
第四,說明使用Struts框架給這個項目帶來哪些痛點?
第五,和Spring框架相比,你感覺各自有什麼優缺點,請結合項目說明,不要空談。
當你學好了Spring框架後就知道該怎麼說?
第六,你的項目經常會擴展,業務實現方式也經常會變更,結合Struts框架說明一旦出現變更了你需要做哪些事?
比如需要更改業務,你該更改哪些文件?一旦更改了代碼,如何部署到伺服器上?
第七,這個項目的訪問量是多少?最高的併發訪問量能達到多少?Struts框架能否很好地處理高併發的情況?
關於常用的技術問題,在本章里都已經提到,我們通過如下問題點來對本章做個總結。
①在Struts2里,如何實現一個Action?
②怎麼指定進入Action後該調用哪個方法?
③定義驗證器的步驟是什麼?
④定義攔截器的步驟是什麼?
⑤Struts2中的type類型有哪些?如果不寫type,預設是什麼?
⑥如何通過配置type類型,實現一個Action往另外一個Action的跳轉?
⑦描述下Struts MVC的工作流程和開發模式。
⑧和JSP+Servlet+JavaBean的開發模式相比,Struts MVC有哪些好處,同時,說明下Struts框架有哪些不足。