Struts2框架 一、什麼是Struts2 Struts2是一個基於MVC設計模式的Web應用框架,它本質上相當於一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的數據交互。Struts 2是Struts的下一代產品,是在 struts 1和 ...
Struts2框架
一、什麼是Struts2
Struts2是一個基於MVC設計模式的Web應用框架,它本質上相當於一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的數據交互。Struts 2是Struts的下一代產品,是在 struts 1和WebWork的技術基礎上進行了合併的全新的Struts 2框架。其全新的Struts 2的體繫結構與Struts 1的體繫結構差別巨大。Struts 2以WebWork為核心,採用攔截器的機制來處理用戶的請求,這樣的設計也使得業務邏輯控制器能夠與ServletAPI完全脫離開,所以Struts 2可以理解為WebWork的更新產品。雖然從Struts 1到Struts 2有著太大的變化,但是相對於WebWork,Struts 2的變化很小。
-------------百度百科
二、快速搭建環境
1、下載官方的jar包
官網:https://struts.apache.org/
2、將其中核心j包中app文件中的struts-blank尾碼名改為zip,併進行解壓其中的lib文件夾中的jar包為運行struts2的基本jar包
3、創建新工程,將基本jar包拷入新建工程中的lib文件夾
4、在web.xml文件中配置核心過濾器
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
其中<filter-class>中過濾器名是根據StrutsPrepareAndExecuteFilter類所在路徑來配置的
5、配置用於跳轉的頁面,此頁面是用於跳轉到Action中
<body> <h3>快速入門</h3> <a href="${pageContext.request.contextPath}/hello.action">跳轉</a> </body>
6、編寫Action
package com.clj.action; /** * Struts框架都使用Action類處理用戶的請求 * @author Administrator * */ public class HelloAction { /** * Action類中的方法簽名有要求,必須這麼做 * public 公共的 * 必須有返回值,必須String類型 * 方法名稱可以是任意的,但是不能有參數列表 * 頁面的跳轉:1.return 字元串 2.需要在struts.xml配置文件中,配置跳轉的頁面 */ public String sayHello(){ //編寫代碼接收請求的參數 System.out.println("Hello Struts2!"); return "success"; } /** * 演示的method方法的預設值 * 當配置文件中沒有配置指定的method方法,他會預設執行Action中的execute方法 * @return */ public String execute(){ System.out.println("method方法的預設值是execute"); return null; } }
7、設置Action配置文件
配置文件是一個xml文件,命名強製為struts2.xml,否則當伺服器開啟時無法識別不是該文件名的配置文件,該文件建立在src下
其中,其約束條件可以在struts2-blank\WEB-INF\src\java的struts2.xml文件中可以找到
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- 包結構 ,包名不能重覆 --> <package name="default" namespace="/" extends="struts-default"> <!-- 配置Action --> <action name="hello" class="com.clj.action.HelloAction" method="sayHello"> <!-- 配置跳轉的頁面,路經的寫法:在Struts2框架中,不管是轉發還是重定向,都不需要加項目名 name表示結果頁面邏輯視圖名稱 type 結果類型(轉發類型,預設值是轉發,也可以設置其他的值) --> <result name="success">/jsp/success.jsp</result> </action> </package> </struts>
8、此時訪問跳轉頁面,即可執行execute方法
總結:這裡當執行跳轉頁面時,會根據配置文件中的action 中的 name屬性進行匹配,找到有name為hello 匹配到HelloAction類中的sayHello方法,通過方法反射等進行解析,根據方法的返回值“success”進行頁面的跳轉,過渡到success.jsp中。值得註意的是,其package包名不能重覆!
三、struts2執行流程為:
從客戶端發送請求過來 先經過前端控制器(核心過濾器StrutsPrepareAndExecuteFilter)過濾器中執行一組攔截器(一組攔截器 就會完成部分功能代碼)執行目標Action,在Action中返回一個結果視圖,根據Result的配置進行頁面的跳轉.
四、Action書寫方式
方式一:POJO類
/** * 三種action書寫方式之POJO類 * 沒有任何繼承和實現 * @author Administrator * */ public class Demo1Action { /** * execute是預設方法 * return null:他不會跳轉 * @return */ public String execute(){ System.out.println("Demo1Action就是一個POJO類"); return null; } }
方式二:實現Action的介面
import com.opensymphony.xwork2.Action; /** * 三種action書寫方式之實現Action的介面,Action是框架提供的介面 * @author Administrator * */ public class Demo2Action implements Action{ @Override public String execute() throws Exception { // TODO Auto-generated method stub System.out.println("Demo2Actions實現了Action的介面"); //Action介面中定義了 public static final String SUCCESS = "success"; return SUCCESS; } }
其中Action有自定義的字元串常量,實現Action的介面返回值就可調用它的常量
方式三:繼承ActionSupport類
import com.opensymphony.xwork2.ActionSupport; /** * 三種action書寫方式之編寫ActionSupport類 * ActionSupport類已經實現了Action和一些其他介面 * @author Administrator * */ public class Demo3Action extends ActionSupport{ public String execute() throws Exception { System.out.println("Demo3Action繼承了ActionSupport類。。"); //不跳轉 return NONE; } }
五、struts2中常量的配置
1.這裡先看看當框架執行時配置文件載入的順序
查看StrutsPrepareAndExecuteFilter:(核心過濾器)兩個功能 :預處理 和 執行
在預處理功能中 init 方法中會有載入配置文件的代碼:
dispatcher.init();
init_DefaultProperties(); // [1] ---- 載入org.apache.struts.default.properties.配置的是struts2的所有常量.
init_TraditionalXmlConfigurations(); // [2] ---- 載入struts-default.xml、struts-plugin.xml、struts.xml
init_LegacyStrutsProperties(); // [3] ---- 載入用戶自定義struts.properties
init_CustomConfigurationProviders(); // [5] ---- 載入Struts2定義Bean.
init_FilterInitParameters() ; // [6] ---- 載入web.xml
init_AliasStandardObjects() ; // [7] ---- 用戶自定義Bean
結論:
* 先載入default.properties文件,在org/apache/struts2/default.properties文件,都是常量。
* 又載入struts-default.xml配置文件,在核心的jar包最下方,struts2框架的核心功能都是在該配置文件中配置的。
* 再載入struts.xml的配置文件,在src的目錄下,代表用戶自己配置的配置文件
* 最後載入web.xml的配置文件
* 後載入的配置文件會覆蓋掉之前載入的配置文件(在這些配置文件中可以配置常量)
因此: 後配置的常量 會 覆蓋先配置的常量.
2、查看常量
3、修改常量
註意:修改常量不能在default.properties文件中修改,可以在struts.xml或者web.xml文件中
1)在struts.xml中
<!-- 編寫常量 --> <constant name="struts.action.extension" value="action,,"></constant>
struts2中訪問action預設尾碼為action或者不寫,這個配置可以進行更改
2)在web.xml中
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> <!-- 編寫常量 --> <init-param> <param-name>struts.action.extension</param-name> <param-value>action,,</param-value> </init-param> </filter>
六、Action的訪問方式
1、傳統方式:配置method屬性
1)Action類代碼
import com.opensymphony.xwork2.ActionSupport; /** * 編寫的客戶的Action的類 * @author Administrator * */ public class CustomerAction extends ActionSupport { //保存客戶 public String save(){ System.out.println("保存客戶"); return NONE; } //刪除客戶 public String delete(){ System.out.println("刪除客戶"); return NONE; } }
2)配置文件配置
<!-- 演示Action的訪問,傳統方式 --> <package name="demo2" namespace="/" extends="struts-default"> <action name="saveCust" class="com.clj.action2.CustomerAction" method="save"/> <action name="delCust" class="com.clj.action2.CustomerAction" method="delete"/> </package>
2、通配符的配置
1)Action類
import com.opensymphony.xwork2.ActionSupport; /** * 通配符的方式 * @author Administrator * */ public class LinkmanAction extends ActionSupport{ //保存客戶 public String save(){ System.out.println("保存聯繫人。。"); return "saveOk"; } //刪除客戶 public String delete(){ System.out.println("刪除聯繫人。。"); return "delOk"; } }
2)配置文件
<!-- 演示Action的訪問,通配符方式 --> <package name="demo3" namespace="/" extends="struts-default">
<!--這裡的{1}代表統配第一個*;若name=”linkman_*_*...methd=”{1}”,{1}表示匹配第一個*,{2}表示通配第二個*,即數字表示*的位置,通過通配符調用指定方法-->
<action name="linkman_*" class="com.clj.action2.LinkmanAction" method="{1}"> <result name="saveOk">/jsp/success.jsp</result> <result name="delOk">/jsp/success.jsp</result> </action> </package>
3、動態方法訪問
1)開啟動態訪問常量(註意:常量的配置都是定義在包外)
<!-- 開啟動態方法訪問 --> <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
2)Action類
import com.opensymphony.xwork2.ActionSupport; /** * 動態方法訪問 * @author Administrator * */ public class UserAction extends ActionSupport{ //保存客戶 public String save(){ System.out.println("保存用戶。。"); return NONE; } //刪除客戶 public String delete(){ System.out.println("刪除用戶。。"); return NONE; } }
3)配置文件
<!-- 演示Action的訪問,動態方法訪問方式 --> <package name="demo4" namespace="/" extends="struts-default"> <action name="user" class="com.clj.action2.UserAction"/> </package>
4)功能變數名稱書寫方式
<h3>動態方法訪問</h3> <a href="${pageContext.request.contextPath}/user!save.action">保存聯繫人</a></br> <a href="${pageContext.request.contextPath}/user!delete.action">刪除聯繫人</a></br>
七、struts2中數據的之接收數據
struts2怎麼傳值呢,這裡由兩種種方式
1.定義一個jsp,利用表單進行數據傳輸
<form action="${pageContext.request.contextPath}/demo1Action.action" method="post"> 姓名:<input type="text" name="username"/></br> 密碼:<input type="password" name="psssword"/></br> <input type="submit" value="註冊"/> </form></br>
2.編寫Action
1)方式一:完全解耦合的方式
package com.clj.demo1; import java.util.Arrays; import java.util.Map; import java.util.Set; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; /** * 完全解耦合的方式,使用Servlet的API * 通過操作ActionContexte類的方法來獲取Servlet API * @author Administrator * */ public class demo1Action extends ActionSupport{ public String execute() throws Exception{ //完全解耦合的方式 ActionContext context=ActionContext.getContext(); //獲取到請求的參數,獲取到所有請求的參數 Map<String,Object> map=context.getParameters(); //遍歷獲取數據 Set<String> keys=map.keySet(); for(String key:keys){ //通過key獲取到值 String[] values=(String[]) map.get(key); //預設情況下,struts已經將字元編碼轉成了UTF-8,無需考慮中文亂碼 System.out.println(key+":"+Arrays.toString(values)); } //如果向request對象中存入值 context.put("msg","小東東"); //獲取其他map集合 context.getSession().put("msg","小蒼"); context.getApplication().put("msg","小澤"); return SUCCESS; } }
2)方式二:原生的Servlet API
package com.clj.demo1; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /** * 原生的Servlet API * @author Administrator * */ public class demo2Action extends ActionSupport{ public String execute() throws Exception{ //獲取到request對象 HttpServletRequest request=ServletActionContext.getRequest(); request.setAttribute("msg","小東東"); request.getSession().setAttribute("msg","美美"); ServletActionContext.getServletContext().setAttribute("msg", "小風"); return SUCCESS; } }
八、結果視圖處理
1、關於全局頁面
為什麼要設置全局頁面?因為在同一個包下,有時候多個action中所獲得的結果視圖是同一個,這要每個action配置會覺得冗餘,此時可以配置個全局頁面來共用
列子:假如當action中方法返回值是success,此時就可無需配置<result>標簽,它會自動跳轉到success.jsp中
<!-- 配置全局的結果頁面,必須定義在當前包下 --> <global-results> <!-- type:轉發類型 dispatcher:轉發,預設值,Action->jsp redirect: 重定向,Action->jsp chain: 多個action之間跳轉,Action->Action(轉發) redirectAction:多個action之間跳轉,Action->Action(重定向) stream:文件下載 --> <result name="success" type="dispatcher">/jsp/success.jsp</result> </global-results>
2.、結果頁面的類型
* 結果頁面使用<result>標簽進行配置,包含兩個屬性
> name -- 邏輯視圖的名稱
> type -- 跳轉的類型,值一些,需要掌握一些常用的類型。常見的結果類型去struts-default.xml中查找。
* dispatcher -- 轉發.type的預設值.Action--->JSP
* redirect -- 重定向. Action--->JSP
* chain -- 多個action之間跳轉.從一個Action轉發到另一個Action. Action---Action
* redirectAction -- 多個action之間跳轉.從一個Action重定向到另一個Action. Action---Action
* stream -- 文件下載時候使用的
演示重定向
action
package com.clj.demo1; import com.opensymphony.xwork2.ActionSupport; /** * 演示重定向 * @author Administrator * */ public class demo3Action extends ActionSupport{ public String save(){ System.out.println("保存"); return SUCCESS; } public String update(){ System.out.println("更新成功"); return NONE; } }
配置文件
<!-- 演示重定向到Action -->
<action name="demo3Action_*" class="com.clj.demo1.demo3Action" method="{1}">
<result name="success" type="redirectAction">demo3Action_update</result>
</action>
九、關於struts2框架數據的封裝
封裝數據是為了便於開發,降低維護成本,這裡有兩種方式進行數據的封裝。
> 提供對應屬性的set方法進行數據的封裝。
* 表單的哪些屬性需要封裝數據,那麼在對應的Action類中提供該屬性的set方法即可。
* 表單中的數據提交,最終找到Action類中的setXxx的方法,最後賦值給全局變數。
1、方式一:屬性驅動
1)屬性驅動寫法方式一:在action類中直接寫從頁面中提交的屬性數據
jsp:
<h3>屬性驅動的方式(直接在Action中編寫屬性)</h3> <!-- aciton接收表單值時屬性要提供set方法 --> <form action="${pageContext.request.contextPath}/regist1.action" method="post"> 姓名:<input type="text" name="username"/></br> 密碼:<input type="password" name="password"/></br> 年齡:<input type="text" name="age"/></br> <input type="submit" value="註冊"/> </form></br>
action:
package com.clj.demo2; import com.opensymphony.xwork2.ActionSupport; /** * 屬性驅動 * 方式一:將Action當做JavaBean類 * @author Administrator * */ public class regist1 extends ActionSupport{ private String username; private String password; private Integer age; //傳值,用set,不需要要用get public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAge(Integer age) { this.age = age; } public String execute() throws Exception{ System.out.println(username+" "+password+" "+age); return NONE; } }
2)屬性驅動寫法方式二:將提交的數據封裝到一個類中
jsp
<h3>屬性驅動的方式(把數據封裝到javaBean中)</h3> <!-- 註意:頁面的編寫風格為OGNL表達寫法 --> <form action="${pageContext.request.contextPath}/regist2.action" method="post"> <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) --> 姓名:<input type="text" name="user.username"/></br> 密碼:<input type="password" name="user.password"/></br> 年齡:<input type="text" name="user.age"/></br> <input type="submit" value="註冊"/> </form></br>
javabean
package com.clj.demo2; public class User { private String username; private String password; private Integer age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", age=" + age + "]"; } }
action
package com.clj.demo2; import com.opensymphony.xwork2.ActionSupport; /** * 屬性驅動的方式,將數據封裝到javaBean中 * @author Administrator * */ public class regist2 extends ActionSupport{ //註意:屬性驅動的方式,需要提供get和set方法 private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String execute(){ System.out.println(user); return NONE; } }
* 註意:只提供一個set方法還不夠,必須還需要提供user屬性的get和set方法!!!
> 先調用get方法,判斷一下是否有user對象的實例對象,如果沒有,調用set方法把攔截器創建的對象註入進來,接下直接調用get方法獲取傳過來的參數
拓展:
1.將數據封裝到list中
jsp
<h3>向List集合封裝數據(預設情況下,採用屬性驅動的方式)</h3> <!-- 採用OGNL表達式 --> <form action="${pageContext.request.contextPath}/regist4.action" method="post"> <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) --> 姓名:<input type="text" name="list[0].username"/></br> 密碼:<input type="password" name="list[0].password"/></br> 年齡:<input type="text" name="list[0].age"/></br> 姓名:<input type="text" name="list[1].username"/></br> 密碼:<input type="password" name="list[1].password"/></br> 年齡:<input type="text" name="list[1].age"/></br> <input type="submit" value="註冊"/> </form></br>
action
package com.clj.demo2; import java.util.List; import com.opensymphony.xwork2.ActionSupport; /** * 屬性驅動的方式,把數據封裝List集合中 * @author Administrator * */ public class regist4 extends ActionSupport{ private List<User> list; public List<User> getList() { return list; } public void setList(List<User> list) { this.list = list; } public String execute() throws Exception{ for(User user:list){ System.out.println(user); } return NONE; } }
2.將數據封裝到Map集合中
jsp
<h3>向Map集合封裝數據(預設情況下,採用屬性驅動的方式)</h3> <!-- 採用OGNL表達式 --> <form action="${pageContext.request.contextPath}/regist5.action" method="post"> <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) --> 姓名:<input type="text" name="map['one'].username"/></br> 密碼:<input type="password" name="map['one'].password"/></br> 年齡:<input type="text" name="map['one'].age"/></br> 姓名:<input type="text" name="map['two'].username"/></br> 密碼:<input type="password" name="map['two'].password"/></br> 年齡:<input type="text" name="map['two'].age"/></br> <input type="submit" value="註冊"/> </form></br>
action
package com.clj.demo2; import java.util.HashMap; import java.util.List; import java.util.Map; import com.opensymphony.xwork2.ActionSupport; /** * 屬性驅動的方式,把數據封裝Map集合中 * @author Administrator * */ public class regist5 extends ActionSupport{ private Map<String,User> map=new HashMap<String,User>(); public Map<String, User> getMap() { return map; } public void setMap(Map<String, User> map) { this.map = map; } public String execute() throws Exception{ System.out.println(map); return NONE; } }
2、方式二:模型驅動
1)手動實例化javabean
private User user=new User();
2)實現ModelDriven<T>介面,實現getModel()的方法,在getModel()方法中返回javaBean
jsp
<h3>模型驅動</h3> <form action="${pageContext.request.contextPath}/regist3.action" method="post"> <!-- 這裡的"user"是根據Aciton中封裝屬性的類的名稱屬性而定(private User user;) --> 姓名:<input type="text" name="username"/></br> 密碼:<input type="password" name="password"/></br> 年齡:<input type="text" name="age"/></br> <input type="submit" value="註冊"/> </form></br>
action代碼
package com.clj.demo2; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; /** * 模型驅動的方式 * 1.實現ModelDrivern介面 * 2.必須手動實列化對象(需要自己new好) * @author Administrator */ public class regist3 extends ActionSupport implements ModelDriven<User>{ //必須手動實例化 private User user=new User(); //獲取模型對象 public User getModel() { // TODO Auto-generated method stub return user; } public String execute() throws Exception{ System.out.println(user); return NONE; } }
十、struts2之攔截器
1、什麼是攔截器?
java里的攔截器是動態攔截Action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的代碼,也可以在一個action執行前阻止其執行,同時也提供了一種可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中攔截器用於在某個方法或欄位被訪問之前,進行攔截然後在之前或之後加入某些操作。
---------360百科
攔截器的利用是struts2框架的一個特點,即面向切麵編程
2、編寫一個攔截器
1)定義一個攔截器類
package com.itheima.interceptior; import com.google.common.collect.AbstractIterator; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * 編寫簡單的攔截器 * @author Administrator * */ public class DemoInterceptor extends AbstractInterceptor{ /** * intercept用來進行攔截的 */ public String intercept(ActionInvocation invocation) throws Exception { System.out.println("Action方法執行之前"); //執行下一個攔截器 String result=invocation.invoke(); System.out.println("Action方法執行之後"); return result; } }
2) 定義一個action
package com.clj.demo3; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport{ public String execute() throws Exception{ System.out.println("我是Action,我正常執行了"); return NONE; } }
3) 配置文件配置
<package name="demo3" extends="struts-default" namespace="/"> <interceptors> <!-- 定義了攔截器 --> <interceptor name="DemoInterceptor" class="com.itheima.interceptior.DemoInterceptor"/> </interceptors> <action name="userAction" class="com.clj.demo3.UserAction"> <!-- 只要是引用自己的攔截器,預設棧的攔截器就不執行了 ,必須手動引入預設棧--> <interceptor-ref name="DemoInterceptor"/> <interceptor-ref name="defaultStack"/> </action> </package>
3、使用攔截器驗證用戶登錄
1) 定義一個攔截器
package com.clj.interceptor; import org.apache.struts2.ServletActionContext; import com.heima.domain.User; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; /** * 自動以攔截器,判斷當前系統是否已經登錄,如果登錄,繼續執行 * 如果沒有登錄,跳轉到登錄頁面 * AbstractInterceptor會攔截所有方法 * MethodFilterInterceptor可以對指定的方法進行攔截 * @author Administrator * */ public class UserInterceptor extends MethodFilterInterceptor{ /** * 進行攔截的方法 */ protected String doIntercept(ActionInvocation invocation) throws Exception { //獲得session對象 User user=(User) ServletActionContext.getRequest().getSession().getAttribute("existUser"); if(user==null){ //沒有登錄,返回字元串,後面就不會執行了 return "login"; } return invocation.invoke(); } }
2)action
package com.itheima.action; import java.lang.reflect.InvocationTargetException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.beanutils.BeanUtils; import org.apache.struts2.ServletActionContext; import com.heima.domain.User; import com.itheima.service.UserService; import com.opensymphony.xwork2.ActionSupport; /** * 用戶登錄模塊控制器 * @author Administrator * */ public class UserAction extends ActionSupport{ /** * 處理登錄功能 * @return */ public String log