第一節: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的變化很小。
Struts擁有優良的設計和功能,其優勢具體如下:
- 項目開源,使用及拓展方便,天生優勢。
- 提供Exception處理機制。
- Result方式的頁面導航,通過Result標簽很方便的實現重定向和頁面跳轉。
- 通過簡單、集中的配置來調度業務類,使得配置和修改都非常容易。
- 提供簡單、統一的表達式語音來訪問所有可供訪問的數據。
- 提供標準、強大的驗證框架和國際框架。
- 提供強大的、可以有效減少頁面代碼的標簽。
- 提供良好的Ajax支持。
- 擁有簡單的插件,只需要放入相應的JAR包,任何人都可以擴展Struts2框架,比如自定義攔截器、自定義結果類型、自定義標簽等,為Struts定制需要的功能,不需要什么特殊配置,并且可以發布給其他人使用。
- 擁有智能的默認設置,不需要另外進行繁瑣的設置。使用默認設置就可以完成大多數項目程序開發所需要的功能。
1.1 前端控制器模式
什么是前端控制器模式呢?如下圖
在圖中傳統方式的開發,有一次請求就會對應一個Servlet。這樣會導致出現很多Servlet。而Struts2將所有的請求都先經過一個前端控制器,在前端控制器中實現框架的部分功能,剩下具體操作要提交到具體的Action中。那么所有的請求都會經過前端控制器,那用什么來實現前端控制器呢?過濾器就是最好的一個實現方式,因為需要所有的請求都可以被過濾器攔截,然后在過濾器中實現部分功能。
第二節:Struts2的入門(下載、集成、配置、運行)
2.1下載
下載地址:https://struts.apache.org/
2.2 jar包結構分析
解壓后的目錄詳情:
- apps:該文件夾用于存放官方提供的Struts2示例程序,這些程序可以作為學習者的學習資料,可為學習者提供很好的參照。各示例均為war文件,可以通過zip方法進行解壓。
- docs:該文件夾用于存放官方提供的Struts2文檔,包括Struts2的快速入門、Struts2的文檔以及API文檔等內容。
- lib:存放Struts2的核心類庫,以及Struts2的第三方插件類庫。
- src:用于存放該版本Struts2框架所對應的源代碼。
2.3 創建web項目,引入Struts2jar包
將示例工程struts-2.5.20\apps\struts2-rest-showcase\WEB-INF\lib下的jar包引入項目中。
部分jar包的作用
- asm :操作java字節碼的類庫
- asm-commons:提供基于事件的表現形式
- asm-tree:提供基于對象的表現形式
- struts-core:Struts2框架的核心類庫
- xwork-core:webWork核心庫,Struts2的構建集成
- ognl:對象圖導航語言(Object Graph Navigation Lanaguage),Struts2框架通過其讀寫對象的屬性。
- freemarker:Struts2標簽模板使用的類庫
- javassist:javaScript字節碼解釋器
- commons-fileupload:Struts2文件上傳組件依賴包
- common-long:包含一些數據類型工具,是對java.lang包的增強
- log4j-api:Struts2的日志管理組件依賴包的api
- log4j-core:Struts2的日志管理組件依賴包
2.4 測試頁面Jsp的編寫
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Struts2初識</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/Struts2Demo.action">點擊訪問第一個action</a>
</body>
</html>
2.5 測試Action的編寫
在src下建立Struts2Demo的文件,并且創建一個public的execute的方法。返回一個"success"的字符串與struts.xml中的result標簽配合,實現頁面的跳轉。
public class Struts2Demo {
/**
* 不提供任何參數,供反射使用
* @return
*/
public String execute(){
System.out.println("我的第一個action訪問成功了。");
return "success";
}
}
Action類編寫好了以后,Struts2框架如何識別它就是一個Action呢,則需要我們隊Action類進行配置。
2.6 Action的配置:
在示例代碼WEB-INF的classes中,有一個名稱為struts.xml的文件,這個文件就是struts2的配置文件。
我們在開發中需要將struts.xml文件引入到工程的src下,配置的內容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- Overwrite Convention -->
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<!--配置一個包-->
<package name="strutsDemo1" extends="struts-default" namespace="/">
<!--配置Action-->
<action name="Struts2Demo" class="com.seapp.struts2.action.Struts2Demo">
</action>
</package>
</struts>
Action類已經配置好了,但現在還不可以執行。因為之前我們介紹過,WEB層框架都有一個特點就是基于前端控制器的模式,這個前端控制器是由過濾器實現的,所以我們需要配置Struts2的核心過濾器。這個過濾器的名稱是StrutsPrepareAndExecuteFilter。
2.7 配置核心過濾器
Struts2框架要想執行,所有的請求都需要經過這個前端控制器(核心過濾器),所以需要配置這個核心過濾器。因為這個過濾器完成了框架的部分的功能。在web.xml中具體的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置Struts2的過濾器(核心控制器)-->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2.8 配置結果頁面跳轉,并訪問action
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!--配置一個包-->
<package name="strutsDemo1" extends="struts-default" namespace="/">
<!--配置Action-->
<action name="struts2Demo" class="com.seapp.struts2.action.struts2Demo">
<result name="success">/demo01.jsp</result>
</action>
</package>
</struts>
訪問http://localhost:8080/struts2/index.jsp后跳轉到如下界面:
點擊跳轉后,進入如下界面:
至此,入門測試項目完成。
2.9 總結:
在測試階段碰到如下問題:在通過配置文件struts.xml來訪問struts2Demo的action時報錯:“Wrong method was defined as an action method: index”,參考https://blog.csdn.net/github_38616039/article/details/79045432該文檔解決問題。根據文檔描述是因為“struts2-rest-plugin-2.5.20.jar”包導致,將其移除后可正常訪問。暫不明白具體原因,望各位前輩指點.....
第二節:Struts2的開發流程分析
從客戶端發送請求過來,先經過前端控制器(核心過濾器StrutsPrepareAndExecuteFilter)過濾器中執行一組攔截器(一組攔截器,就會玩部分功能代碼),在Struts2中定義很多攔截器,在其默認棧中的攔截器會得到執行。
第三節:Struts2的常見配置
在針對Struts2中的struts.xml文件的配置等內容進行詳細的學習前,先對Struts2的配置文件的加載順序有一定的了解,這樣對后面學習Struts2的配置都是有幫助的。
3.1 Struts2配置文件的加載順序
每次從客戶端發送請求到服務器都要先經過Struts2的核心過濾器StrutsPrepareAndExecuteFilter,這個過濾器有兩個功能:預處理和執行。在預處理中主要就是來加載配置文件的。對應的是過濾器中的init方法,而執行是用來執行一組攔截器完成部分功能的,對應的是過濾器的doFilter方法。所以要了解Struts2的配置文件的加載順序,需要從過濾器的init方法開始:
在init方法中,調用了init的initDispacher的方法來加載配置文件,進入該方法:
在該方法中又調用了dispatcher的init方法,繼續往下跟蹤:
這一系列代碼就是用來截止Struts2的配置文件的:
- this.init_DefaultProperties():加載org.apache.struts.default.properties配置,是Struts2的所有常量。
- this.init_TraditionalXmlConfigurations():加載struts-default.xml,struts-plugin.xml,struts.xml的配置項。
- this.init_LegacyStrutsProperties():加載用戶自定義的struts.properties.
- this.init_CustomConfigurationProviders():加載用戶配置的提供對象
- this.init_FilterInitParameters():加載web.xml
- this.init_AliasStandardObjects():加載標準對象。
根據上面的代碼我們可以得出配置文件的加載順序如下:
/******
* default.properties
* struts-default.xml
* struts-plugin.xml
* struts.xml 配置Action以及常量
* struts.properties 配置常量
* web.xml 配置核心過濾器以及常量
******/
前三個配置文件我們不關心,是Struts2內部的配置文件,我們無法修改,能修改的文件就是struts.xml,struts.properties,web.xml配置文件。這三個配置文件的加載是有順序的,這三個配置文件都可以修改Struts2常量的值,要記住的是,后加載配置文件中常量的值會將先加載的配置文件中的常量的值給覆蓋。
3.2 Action的配置
Struts2框架的核心配置文件是struts.xml文件,該文件主要用來配置Action的請求和對應關系。
<package>標簽的配置
Struts2框架的核心組件是Action和攔截器,它使用包來管理Action和攔截器。每個包就是多個Action,多個攔截器,多個攔截器引用的集合。在struts.xml文件中,package元素用于定義包配置,每個package元素定義一個包配置。package元素的常用屬性,如表所示:
其中在配置包時,必須制定name屬性,就是包的標識。除此之外,還可以指定一個可選的extends屬性,extends屬性值必須是另一個包的name屬性值,但該屬性值通常都設置為struts-default,這個該包中的action就具有了Struts2框架默認的攔截器等功能了。Struts2還提供了一種所謂的抽象包,抽象包不能包含Action定義。為了顯示指定一個包是抽象包,可以為該package元素增加abstract = "true"屬性。
在package中還有namespace的配置,namespace屬性與action標簽的name屬性共同決定了訪問路徑。namespace有如下三種配置:
- 默認名稱空間:默認名稱空間就是namespace=""。
- 根名稱空間:根名稱空間就是namespace = "/"
- 帶名稱的名稱空間:帶名稱的名稱空間就是namespace = "/demo1"
<Action>的配置
Action映射是框架中的基本“工作單元”。Action映射就是講一個請求的URL映射到一個Action類,當一個請求匹配某個Action名稱時,框架就使用這個映射來確定如何處理請求。在sturts.xml文件中,通過<action>元素對請求的Action和Action類進行配置。
<action>元素中共有4個屬性,這4個屬性的說明如下:
其中name屬性和namespace屬性共同決定了訪問路徑,class對應的是Action類的全路徑。Method指定了執行Action的那個方法,默認是execute方法。
第四節:Struts2常量的配置:
Sturts2中的常量大多在默認配置文件中已經配置好了,但根據用戶需求的不同,開發的要求也不同,可能需要修改這些常量值,修改的方法就是在配置文件中對常量進行重新配置。
Struts2常量配置共有3種方式,分別如下:
- 在struts.xml文件中使用<constant>元素配置常量。
- 在struts.properties文件中配置常量。
- 在web.xml文件中通過<init-param>元素配置常量。
具體如下:
4.1 在Struts.xml文件中通過<constant>元素配置常量
在struts.xml文件中通過<constant>元素來配置常量,是最常用的方式。在struts.xml文件中通過<constant.../>元素來配置常量時,需要指定兩個必填的屬性name和value。
- name:該屬性指定了常量的常量名。
- value:該屬性指定了常量的常量值。
在struts.xml文件中配置的示例代碼如下:
<!--設置默認編碼集為UTF-8-->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!--設置開發模式-->
<constant name="struts.devMode" value="true"/>
值的一提的是,struts.properties文件能配置的常量都可以在struts.xml文件中用<constant>元素來配置。
4.2 在struts.properties文件中配置常量
struts.properties文件是一個標準的properties文件,其格式是key-value對,即每個key對應一個value,key表示的是Struts2框架中的常量,而value則是其常量值。在struts.properties文件中配置常量的方式
# 設置默認編碼集為UTF-8
struts.il8n.encoding=UTF-8
# 設置action請求拓展名為action或者沒有拓展名
struts.action.extension=action
# 設置不使用開發模式
struts.devMode = false
# 設置不開啟動態方法調用
struts.enable.DynamicMethodInvocation = false
需要注意的是,和struts.xml文件一樣,struts.properties文件也應存放于WEB-INF/classes路徑下。
4.3 在web.xml文件中通過初始化參數配置常量
在web.xml文件中配置核心過濾器StrutsPrepareAndExecuteFilter時,通過初始化參數來配置常量。通過<filter>元素的<init-param>子元素指定,每個<init-param>元素配置了一個Struts2常量。在web.xml文件中通過初始化參數配置常量方式,具體如下:
<!--配置Struts2的過濾器(核心控制器)-->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
<!--配置編碼集為UTF-8-->
<init-param>
<param-name>struts.i18n.encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
注意:<init-param>標簽必須放在<filter>標簽下。
Struts2所支持的常量數量眾多,在struts2-core-***.jar壓縮文件的org/apche/struts2路徑下有一個default.properties文件,該文件里為Struts2的所有常量都指定了默認值,我們可以通過查看該文件來了解Struts2所支持的常量。
后加載的配置文件的常量的值會覆蓋先加載的配置文件中常量的值
在實際的開發中我們更習慣使用struts.xml修改struts2的常量。
4.4 分模塊開發的配置
在實際開發中,我們通常很多人都需要修改同一個配置文件就是struts.xml。因為這個文件是struts2的核心配置文件,而且這個文件一旦改錯了一點,那么會導致整個項目都會出現問題,所以struts2提供了<include>標簽解決這個問題。
<include>元素用來在一個struts.xml配置文件中包含其他的配置文件,包含配置體現的是軟件工程中的“分而治之”原則。Struts2允許將一個配置文件分解成多個配置文件,從而提供配置文件的可讀性。Struts2默認只加載WEB-INF/classes下的strts.xml文件,但一旦通過多個xml文件來配置Action,就必須通過struts.xml文件來包含其他配置文件。
具體示例如下:
<struts>
<!--不指定默認路徑在src下時的方式 -->
<include file="struts-shop.xml"/>
<!-- 配置文件在具體包中時的方式 -->
<include file = "com/seapp/struts2/action/strurs-product.xml"/>
</struts>
需要注意的是:每一個被包含的配置文件都是標準的Struts2配置文件,一樣包含DTD信息、Struts2配置文件的根元素信息等。通常將Struts2的所有配置文件都放在Web項目的WEB-INF/classes路徑下,struts.xml文件包含了其他配置文件,在Struts2框架自動加載struts.xml文件時,完成加載所有的配置信息。
第五節:Struts2中Action類的創建
在Struts2的應用開發中,Action作為框架的核心類,實現對用戶請求的處理,Action類被稱為業務邏輯控制器。一個Action類代表一次請求或調用,每個請求的動作都對應于一個相應的Action類,一個Action類是一個對立的工作單元。也就是說,用戶的每次請求,都會轉到一個相應的Action類里面,由這個Action類來進行處理。簡而言之,Action就是用來處理一次用戶請求的對象。
5.1 Action的編寫方式:
Action是一個POJO的類
在Struts2中,Action可以不繼承特殊的類或不實現任何特殊的接口,僅僅是一個POJO。POJO全稱Plain Ordinary Java Object(簡單的Java對象),只是具有一部分getter/setter方法的類,就可以稱作POJO,一般在POJO類中,要有一個公共的無參的構造方法(采用默認的構造方法就可以)和一個execute()方法。定義格式如下:
public class struts2Demo {
/**
* 不提供任何參數,供反射使用
* @return
*/
public String execute(){
System.out.println("我的第一個action訪問成功了。");
return "success";
}
}
execute()方法的要求如下:
- 方法的權限修飾符為public
- 返回一個字符串,就是指示的下一個頁面的Result
- 方法沒有參數
也就是說,滿足上述要求的POJO都可算作是Struts2的Action實現。
Action類實現一個Action的接口
為了讓用戶開發的Action類更規范,Struts2提供了一個Action接口,用戶在實現Action控制類時,可以實現Struts2提供的這個Action接口。
Action接口定義了Struts的Action處理類應該實現的規范,具體如下:
import com.opensymphony.xwork2.Action;
public class Struts2Action01 implements Action {
@Override
public String execute() throws Exception {
return null;
}
}
Action接口位于com.opensymphony.xwork2.Action包中,這個接口中定義了一個execute()方法,該接口的規范規定了Action處理類應該包含一個execute()方法,該方法返回一個字符串。除此之外,該接口還定義了5個字符串常量,它們的作用是統一execute()方法的返回值。
Action接口中提供了5個已經定義的常量如下:
- SUCCESS :success,代表成功。
- NONE : none,代表頁面不跳轉。
- ERROR: error,代表跳轉到錯誤頁面。
- INPUT: input,數據校驗的時候跳轉的路徑。
- LOGIN:login,用來跳轉到登錄頁面。
Action類繼承ActionSupport類 ,開發中通常使用這種方式,示例如下:
import com.opensymphony.xwork2.ActionSupport;
public class Struts2Action02 extends ActionSupport {
@Override
public String execute() throws Exception {
return NONE;
}
}
ActionSupport類本身實現了Action接口,是Struts2中默認的Action接口的實現類,所以繼承ActionSupport就相當于實現了Action接口。ActionSupport類還實現了Validateable,ValidationAware,TextProvider,LocaleProvider和Serializable等接口,來為開發這提供更多的功能。
ActionSupport類中提供了許多默認方法,這些方法包括獲取國際化信息的方法、數據校驗的方法、默認的處理用戶請求的方法等。實際上,ActionSupport類時Struts2默認的Action處理類,讓開發者的Action類繼承ActionSupport類,則會大大簡化Action的開發。
第六節:Action的訪問
Action類的執行訪問可以通過前面的配置來完成,但是之前的方式有一個缺點,就是多次請求不能對應同一個Action。因為實際的開發中,一個模塊的請求通常由一個Action類處理就好,否則就會造成Action類過多。
那么如何讓一個模塊的操作提交到一個Action中呢?在<action>的標簽中有一個屬性method,通過method的配置就可以指定Action中的某個方法執行。
6.1 解決Action的訪問方式一:通過配置method屬性完成。
- 編寫jsp頁面
<%--通過配置method來實現調用方法的切換--%>
<a href="${pageContext.request.contextPath}/addCustomerAction.action">增加用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/updateCustomerAction.action">更新用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/delCustomerAction.action">刪除用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/queryCustomerAction.action">查詢用戶</a>
<br/>
- 在struts.xml配置文件中,增加method的配置
<action name="addCustomerAction" class="com.seapp.struts2.action.Struts2Action02" method="add">
</action>
<action name="updateCustomerAction" class="com.seapp.struts2.action.Struts2Action02" method="update">
</action>
<action name="delCustomerAction" class="com.seapp.struts2.action.Struts2Action02" method="del">
</action>
<action name="queryCustomerAction" class="com.seapp.struts2.action.Struts2Action02" method="query">
</action>
- 在Struts2Action02中,增加對應的方法
import com.opensymphony.xwork2.ActionSupport;
public class Struts2Action02 extends ActionSupport {
@Override
public String execute() throws Exception {
return NONE;
}
/**
* 執行保存用戶的方法
* @return
* @throws Exception
*/
public String add() throws Exception{
System.out.println("用戶保存的方法被調用了");
return NONE;
}
/**
* 執行更新用戶的方法
* @return
* @throws Exception
*/
public String update() throws Exception{
System.out.println("用戶更新的方法被調用了");
return NONE;
} /**
* 執行刪除用戶的方法
* @return
* @throws Exception
*/
public String del() throws Exception{
System.out.println("用戶刪除的方法被調用了");
return NONE;
} /**
* 執行查詢用戶的方法
* @return
* @throws Exception
*/
public String query() throws Exception{
System.out.println("用戶查詢的方法被調用了");
return NONE;
}
}
-
調用執行結果
執行界面如下:
image.png
返回結果:
image.png
但是這種方式我們會發現,同一個Action類被配置了很多次,只是修改了后面的method的值。那么能不能配置簡單化呢?也就是一個Action類,只配置一次就好。這就需要我們使用通配符的配置方式了。
6.2 解決Action的訪問問題方式二:通過配置符的配置完成
- 編寫jsp訪問界面
<%--通過配置通配符的方式實現方法的切換--%>
<h2>通過配置通配符的方式實現方法的切換</h2>
<a href="${pageContext.request.contextPath}/CustomerAction_add.action">增加用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction_update.action">更新用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction_del.action">刪除用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction_query.action">查詢用戶</a>
<br/>
- 在struts.xml配置文件中,關于通配符的配置:
<!--配置一個包-->
<package name="strutsDemo1" extends="struts-default" namespace="/" strict-method-invocation="false">
<!--配置Action-->
<!--使用通配符的方式配置action不同方法的訪問-->
<action name="CustomerAction_*" class="com.seapp.struts2.action.Struts2Action02" method="{1}">
</action>
</package>
注意:struts2從2.5版本開始,為了提升安全性,默認開啟了嚴格的方法調用。如果要使用通配符*,必須在package中設置 strict-method-invocation="false"。在沒有配置的情況下報錯信息會提示
There is no Action mapped for namespace [/] and action name [Action!Metho
-
Struts2Action02中的方法沒變,則訪問測試結果如下:
image.png
6.3 解決Action的訪問問題方式三:動態方法訪問
動態方法訪問在Struts2中默認是不開啟的,如果需要使用則需要卡其一個常量
<!--開啟動態訪問-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
- jsp訪問界面代碼編寫
<%--通過配置動態訪問的方式實現方法的切換--%>
<h2>通過配置動態訪問的方式實現方法的切換</h2>
<a href="${pageContext.request.contextPath}/CustomerAction!add.action">增加用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction!update.action">更新用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction!del.action">刪除用戶</a>
<br/>
<a href="${pageContext.request.contextPath}/CustomerAction!query.action">查詢用戶</a>
<br/>
- struts.xml中action的配置
<!--配置動態訪問action的方式-->
<action name="CustomerAction" class="com.seapp.struts2.action.Struts2Action02">
</action>
-
Struts2Action02中方法不變,則對應的測試結果如下:
image.png