1.struts2 框架介紹
struts2 框架在 struts1 和 WebWork的技術(shù)上合并而來(lái)的手趣,全新的struts2 框架是以 WebWork 框架為核心,采用攔截器的機(jī)制來(lái)處理用戶(hù)請(qǐng)求许布,是一個(gè)基于 MVC 設(shè)計(jì)模式的 Web 框架,本質(zhì)上相當(dāng)于一個(gè) servlet鸳惯,struts2 作為控制器來(lái)建立模型和視圖之間的數(shù)據(jù)交互折欠。
struts2 的開(kāi)發(fā)組經(jīng)常被指責(zé)漏洞的修復(fù)手法不妥,要么修復(fù)后仍有課利用坯辩,要么無(wú)法修復(fù)長(zhǎng)期閑置馁龟,其中 2013 年的 struts 高危漏洞引起了不小的轟動(dòng),導(dǎo)致中國(guó)大陸使用 struts2 的網(wǎng)站被入侵漆魔,建議使用 struts2.3 或者以上的最新版本。本文使用的是 struts2.3.34 版本却音,struts2.5 的版本和 struts2.3 已經(jīng)有不小的跨度了改抡,一些代碼的寫(xiě)法也不同,在學(xué)習(xí)時(shí)要注意版本帶來(lái)的問(wèn)題系瓢。
下面我們通過(guò)一個(gè) java web 項(xiàng)目來(lái)學(xué)習(xí) struts2 的基礎(chǔ)阿纤。
2.搭建 struts2 框架
2.1 準(zhǔn)備 struts2 包
struts2 也是 Apache 基金會(huì)下的一個(gè)開(kāi)源項(xiàng)目,可以直接去官網(wǎng)下載 struts2 的 zip 壓縮包夷陋,建議下載完整版的包欠拾,我下載的是 struts-2.3.34-all.zip,解壓后骗绕,lib 文件下有我們要使用的 jar 包藐窄,但是里面 100 jar 包并不是都必須的,可以在 apps 文件下酬土,找到一個(gè)空白的 war 包荆忍,用壓縮工具打開(kāi),將網(wǎng)站 WEB-INF 目錄下的 lib 文件夾里面的 jar 包復(fù)制到一個(gè)文件下,這些就是我們所需的 struts2 框架的 jar 包刹枉。
2.2 導(dǎo)入 struts2 的 jar 包
不同與一般的 java 項(xiàng)目叽唱,使用 hibernate 時(shí),可以自己建立用戶(hù)庫(kù)微宝,在項(xiàng)目中加入構(gòu)建路徑即可棺亭,使用 struts2 框架不能使用用戶(hù)庫(kù)這種方式,需要將準(zhǔn)備好的 struts2 的 jar 包復(fù)制到 java web 項(xiàng)目下蟋软,一般放在 WEB-INF 文件下的lib 文件夾中侦铜。
3.使用 struts2 框架
3.1 創(chuàng)建 action 類(lèi)
創(chuàng)建一個(gè) java 類(lèi),使它繼承 ActionSupport 類(lèi)钟鸵,重寫(xiě)父類(lèi)的 execute() 方法钉稍,返回值要是 String 類(lèi)型的。
package cc.wenshixin.action;
import com.opensymphony.xwork2.ActionSupport;
public class HelloStruts extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("success");
return "ok";
}
}
3.2 配置 action 的 struts.xml 文件
創(chuàng)建完action類(lèi)棺耍,還要配置一下action訪問(wèn)的路徑贡未,創(chuàng)建 struts2 的核心配置文件 struts.xml,注意這里 struts 后面沒(méi)有 2 蒙袍,并且該文件必須直接放在 src 下面俊卤,也即是 struts2 的核心配置文件名稱(chēng)和位置是固定的。
同 hibernate 框架的映射文件一樣害幅,也需要引入 xml 文件的 dtd 約束消恍。要做的配置放在 struts
標(biāo)簽中,package
是配置一個(gè)包以现,extents
屬性中寫(xiě)struts的默認(rèn)配置值 struts-default
狠怨,namespace
屬性中寫(xiě)/
。package
標(biāo)簽里面的 action
標(biāo)簽是配置 action 類(lèi)的邑遏,name
屬性中寫(xiě)將來(lái)要訪問(wèn)的 action 名稱(chēng)佣赖,class為 action 類(lèi)的全路徑名。action
標(biāo)簽里面是 result
標(biāo)簽记盒,用來(lái)配置根據(jù) action 類(lèi)中方法的返回值跳轉(zhuǎn)的路徑憎蛤,這里寫(xiě)的是一個(gè)jsp文件路徑,/代表網(wǎng)站的根目錄纪吮。
<?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="hellodemo" extends="struts-default" namespace="/">
<action name="hello" class="cc.wenshixin.action.HelloStruts">
<result name="ok">/hello.jsp</result>
</action>
</package>
</struts>
在網(wǎng)站的根目錄下創(chuàng)建一個(gè) hello.jsp文件來(lái)測(cè)試是否可以訪問(wèn)俩檬。
3.3 配置網(wǎng)站的 web.xml 文件
做完上面的操作,就可以啟動(dòng) tomcat 服務(wù)器訪問(wèn)了碾盟,訪問(wèn)路徑 http://localhost:8080/web項(xiàng)目名/hello.action棚辽,得到的是一個(gè) 404 的錯(cuò)誤,我們還需要配置一下整個(gè)網(wǎng)站的 xml 文件巷疼。
welcome-file-list
里面的內(nèi)容是網(wǎng)站歡迎頁(yè)面支持的文件格式晚胡。dispaly-name標(biāo)簽里面是網(wǎng)站項(xiàng)目的名字灵奖。重要的是 filter 過(guò)濾器標(biāo)簽,filter-class里面是struts過(guò)濾器類(lèi)的名稱(chēng)估盘,這個(gè)在 struts2.5 的版本中是不一樣的瓷患。下面的url-pattern標(biāo)簽中 /*
是指攔截所有用戶(hù)進(jìn)行處理。注意 filter-class
標(biāo)簽中的的值要一致遣妥。
再來(lái)訪問(wèn)一下剛才的路徑擅编,出現(xiàn) jsp 文件中的內(nèi)容就說(shuō)明訪問(wèn)成功了。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>struts01</display-name>
<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>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
4.struts2 框架及配置詳解
4.1 struts2 框架設(shè)計(jì)思想
前面提到 struts2 是一個(gè) MVC 的 Web 框架箫踩,Web 層的框架都是基于前端控制器的模式爱态,如下圖所示,傳統(tǒng)的方式境钟,頁(yè)面上的每個(gè)請(qǐng)求锦担,是交給一個(gè)的 servlet 來(lái)處理,這樣就需要寫(xiě)很多的 servlet慨削,雖然可以用反射來(lái)減少代碼量洞渔,但是寫(xiě)起來(lái)還是很繁雜。而現(xiàn)在每個(gè)請(qǐng)求都要經(jīng)過(guò)前端控制器過(guò)濾缚态,在轉(zhuǎn)交給相應(yīng)的 action 處理磁椒,前端控制器也是有過(guò)濾器實(shí)現(xiàn)的。
4.2 struts2 框架執(zhí)行過(guò)程
在上面輸入 http://localhost:8080/web項(xiàng)目名/hello.action玫芦,請(qǐng)求會(huì)先被網(wǎng)站 web.xml 文件中的過(guò)濾器處理浆熔,獲取請(qǐng)求的路徑和得到路徑中的 hello 值,然后在 src 下面找到 struts.xml桥帆,使用 dom4j 解析得到 xml 文件中的內(nèi)容医增,用得到的 hello
值和 action
標(biāo)簽里面的 name
屬性值匹配,匹配成功就找到 name
屬性所在的 action
標(biāo)簽里面的 class
屬性的值环葵,得到 action
類(lèi)的全路徑调窍,使用反射執(zhí)行相應(yīng)的方法實(shí)現(xiàn)功能,然后得到 action
類(lèi)相應(yīng)方法的返回值张遭,和 action
標(biāo)簽中的 result
標(biāo)簽的 name
屬性值匹配,匹配成功就跳轉(zhuǎn)到對(duì)應(yīng)的路徑或者是頁(yè)面地梨。
4.3 struts2 核心配置文件詳解
4.3.1 配置文件的加載順序
在 web.xml 文件中我們?cè)O(shè)置了 struts2 的核心過(guò)濾器:StrutsPrepareAndExecuteFilter菊卷,可以打開(kāi)struts的源代碼查看一下這個(gè)類(lèi)里面的內(nèi)容,這個(gè)過(guò)濾器有兩個(gè)功能宝剖,預(yù)處理和執(zhí)行洁闰,預(yù)處理是加載配置文件,對(duì)應(yīng)的是StrutsPrepareAndExecuteFilter類(lèi)的init()方法万细,而執(zhí)行是用一組攔截器來(lái)完成相應(yīng)的功能扑眉,對(duì)應(yīng)的是該類(lèi)中的doFilter()方法。
相關(guān)代碼:
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config); //查看該方法
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}
}
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init(); //查看該方法
return dispatcher;
}
public void init() {
if (configurationManager == null) {
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
init_FileManager();
//下面加載struts2的配置文件
init_DefaultProperties(); // [1] 加載struts2的所有常量
init_TraditionalXmlConfigurations(); // [2] 加載struts-default.xml文件,struts-plugin.xml文件和struts.xml文件
init_LegacyStrutsProperties(); // [3] 加載用戶(hù)自定義的struts.properties文件
init_CustomConfigurationProviders(); // [5] 加載用戶(hù)配置提供的對(duì)象
init_FilterInitParameters() ; // [6] 加載網(wǎng)站項(xiàng)目的web.xml文件
init_AliasStandardObjects() ; // [7] 加載標(biāo)準(zhǔn)對(duì)象
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckWebLogicWorkaround(container);
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
errorHandler.init(servletContext);
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
從上面可以看出腰素,struts2 的配置文件加載順序?yàn)椋?/p>
- default-properties
- struts-default.xml
- struts-plugin.xml
- struts.xml // 配置action以及常量
- struts.properties //配置常量
- web.xml //配置核心過(guò)濾器以及常量
后面三個(gè)文件都是可以配置常量的聘裁,但是后面文件中的常量值可以把前面同名的常量值覆蓋,這是由配置文件的加載順序決定的弓千。
4.3.2 struts.xml 文件
struts.xml 文件是 struts2 的核心配置文件衡便,其名稱(chēng)和位置都是固定的,在 struts.xml 文件中主要有三個(gè)標(biāo)簽:package
洋访、action
镣陕、result
標(biāo)簽。
- package 標(biāo)簽
類(lèi)似于代碼包姻政,用以區(qū)別不同的 action呆抑,要配置 action,在標(biāo)簽外面首先要加上 package
標(biāo)簽汁展。
package 標(biāo)簽的屬性:
- name 屬性
package
的區(qū)分名鹊碍,一個(gè)配置文件中所有package
的name
屬性值都是要不相同的。
- name 屬性
- extents 屬性
extends="struts-default"
善镰,該屬性的值是固定妹萨,寫(xiě)了這個(gè)屬性之后,package
標(biāo)簽中配置的類(lèi)才有action
的功能
- extents 屬性
- namespace 屬性
namespace 屬性值/
和action
標(biāo)簽里面的值構(gòu)成將來(lái)要訪問(wèn)的路徑炫欺。
- namespace 屬性
action 標(biāo)簽
action
標(biāo)簽用來(lái)配置 action
的訪問(wèn)路徑乎完。
action 標(biāo)簽的屬性:
- name 屬性
同上面一樣,namespace
屬性值/
和action
標(biāo)簽里面的值構(gòu)成將來(lái)要訪問(wèn)的路徑品洛。一個(gè)package
標(biāo)簽可以寫(xiě)多個(gè)action
標(biāo)簽树姨,但是每個(gè)action
中的name
屬性的值都不能相同。
- name 屬性
- class 屬性
action
類(lèi)的全路徑名桥状。
- class 屬性
- method 屬性
action
類(lèi)中要執(zhí)行的方法名帽揪,不寫(xiě)默認(rèn)就是執(zhí)行action
類(lèi)的execute
方法,在action
里面執(zhí)行多個(gè)方法就使用method
屬性來(lái)配置辅斟。
- method 屬性
result 標(biāo)簽
根據(jù) action
中方法的返回值转晰,配置到不同的路徑里面。
result 標(biāo)簽的屬性:
- name 屬性
方法的返回值士飒。
- name 屬性
- type 屬性
配置處理路徑(轉(zhuǎn)發(fā)和重定向)查邢,type
屬性的默認(rèn)值是做轉(zhuǎn)發(fā)操作。
- type 屬性
4.3.3 action 類(lèi)的編寫(xiě)方式
action 類(lèi)的編寫(xiě)方式共有三種:
- 創(chuàng)建普通類(lèi)
action 類(lèi)是可以布集成任何類(lèi)酵幕,也不實(shí)現(xiàn)任何接口扰藕,但是這種方式很少使用。
- 創(chuàng)建普通類(lèi)
- 創(chuàng)建類(lèi)芳撒,實(shí)現(xiàn)action接口
execute() 方法其實(shí)是 Action 接口中的邓深,繼承的 ActionSupport 類(lèi)也是實(shí)現(xiàn)了 Action 接口未桥,但這種方式也很少使用。
- 創(chuàng)建類(lèi)芳撒,實(shí)現(xiàn)action接口
- 創(chuàng)建類(lèi)芥备,繼承ActionmSupport類(lèi)
推薦使用該方式編寫(xiě) action 類(lèi)
- 創(chuàng)建類(lèi)芥备,繼承ActionmSupport類(lèi)
action 類(lèi)中的方法的訪問(wèn)權(quán)限修飾符要是 public冬耿。
4.3.3 action 類(lèi)的訪問(wèn)方法
action 類(lèi)的訪問(wèn)方法也有三種:
- 第一種 使用 action 標(biāo)簽的 method 屬性,在這個(gè)屬性中寫(xiě)上執(zhí)行的 action 方法门躯。
action 類(lèi)的方法是可以沒(méi)有返回值的(將方法的返回值寫(xiě)成 void淆党,將方法的返回值寫(xiě)成"none"
或者常量NONE
),這時(shí)在配置文件中就不需要配置讶凉。但是如果 action 類(lèi)的方法有返回值染乌,而在配置文件中么沒(méi)有配置 result 標(biāo)簽的 name 屬性,訪問(wèn)就會(huì)出現(xiàn)錯(cuò)誤懂讯。另外 action 類(lèi)的方法有返回值荷憋,那么返回值就必須是 String 類(lèi)型的。
使用這種方式訪問(wèn) action 類(lèi)褐望,如果一個(gè) action 類(lèi)中有很多方法勒庄,那么,就要寫(xiě)很多的 action 標(biāo)簽一一匹配瘫里,這時(shí)簡(jiǎn)單的方法就是使用下面通配符的方式配置实蔽。
示例代碼:
在 action 類(lèi)中加上方法
public String update()
{
return "update";
}
在配置文件中加上方法的配置
<!-- 執(zhí)行update方法,相應(yīng)增加所需的頁(yè)面 -->
<action name="update" class="cc.wenshixin.action.HelloStruts" method="update">
<result name="update">/update.jsp</result>
</action>
- 使用通配符方式實(shí)現(xiàn)
在 action 標(biāo)簽的 name 屬性中使用*
來(lái)匹配所有的方法谨读,然后在 method 屬性中使用{1}
來(lái)替代第一個(gè) * 星號(hào)的內(nèi)容局装,這樣不同的 URL 地址就會(huì)統(tǒng)一處理。使用通配符時(shí)開(kāi)發(fā)中訪問(wèn) action 類(lèi)方法的常用方式劳殖。
示例代碼
將 action 方法的返回值修改成一樣的铐尚。
public String update()
{
return "test";
}
public String delete()
{
return "test";
}
<action name="test-*" class="cc.wenshixin.action.HelloStruts" method="{1}">
<result name="test">/test.jsp</result>
</action>
這樣訪問(wèn) test-upate 頁(yè)面和 test-delete 頁(yè)面,得到的返回頁(yè)面就是一樣的哆姻。
- 動(dòng)態(tài)訪問(wèn)方式(不使用)
動(dòng)態(tài)訪問(wèn)方式在 struts2 中默認(rèn)不開(kāi)啟的宣增,如果想使用需要先去開(kāi)啟一個(gè)常量,常量也需要配置矛缨,動(dòng)態(tài)方式訪問(wèn)主要的控制是在頁(yè)面端爹脾,編寫(xiě) action 類(lèi)和配置 action 都很簡(jiǎn)單,關(guān)鍵是訪問(wèn)路徑的編寫(xiě)箕昭。