Struts2

1,Struts2介紹和開發環境的搭建

    Stuts2是在webWork2的基礎發展而來的。和stuts1一樣,Sttuts2也屬於MVC框架。

優點:

1,在軟件設計上Sttuts2沒有像Stuts1那樣跟ServletAPI和strutsAPI有着緊密的耦合,Struts2的應用可以不依賴與ServletAPI和strutsAPI,

Struts2的這種設計屬於無入侵式設計,而struts1屬於入侵式設計。

2,Struts2提供了攔截器,利用攔截器可以進行AOP編程。實現如權限攔截等功能。

3,Struts2提供了類型轉換器,我們可以把特殊的請求參數轉換成需要的類型。在Struts1中,如果我們要實現同樣的功能,就必須向Struts1

的底層實現BeanUtil註冊類型轉換器才行。

4,Struts2提供支持多種表現層技術,如jsp,freeMarker,velocity等。

5,Struts2的輸入校驗可以對指定方法進行校驗,解決了Struts1的長久之痛。

6,提供了全局範圍,包範圍和Action範圍國際化資源文件管理實現

開發環境的搭建

 1,找到開發Struts2應用需要使用到的jar文件

      

2,編寫Struts2配置文件。

       Struts2默認的配置文件爲struts.xml,該文件需要存放在web-inf/classes下,開發的時候,可以放到src目錄下,該文件的配置模板如下:

       

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	
</struts>

       3,在web.xml中加入Struts2MVC框架啓動配置。

  Struts2 1.6配置啓動項    
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
 <!-- 自從Struts 2.1.3以後,下面的FilterDispatcher已經標註爲過時
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> --> 
</filter>
<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


在StrutsPrepareAndExecuteFilter的init()方法中將會讀取類路徑下默認的配置文件struts.xml完成初始化操作。

注意:struts2讀取到struts.xml的內容後,以javabean存放進內存中,以後struts2對用戶的每次請求處理將使用內存中的數據,而不是每次

都讀取struts.xml文件。

2,第一個struts應用

<package name="itcast" namespace="/test" extends="struts-default">

  <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >//名稱作爲訪問action路徑的一部分,class是action類,請求到來後,交給這個action來處理,交給那個方法處理,交給這個方法來指定

  <result name="success">/WEB-INF/page/hello.jsp</result>//視圖的名稱,路徑

  </action>

 </package>

struts2框架中使用包來管理Action,包的作用和java中的類包是非常類似的,它主要用於管理一組業務功能相關的action。在實際應用中,我們應該把一組業務功能相關的Action放在同一個包下。

配置包時必須指定name屬性,該name屬性值可以任意取名,但必須唯一,他不對應java的類包,如果其他包要繼承該包,必須通過該屬性進行引用。包的namespace屬性用於定義該包的命名空間,命名空間作爲訪問該包下Action的路徑的一部分,減少重複的代碼。如訪問上面例子的Action,訪問路徑爲:/test/helloworld.action namespace屬性可以不配置,對本例而言,如果不指定該屬性,默認的命名空間爲“”(空字符串

通常每個包都應該繼承struts-default包,因爲Struts2很多核心的功能都是攔截器來實現。如:從請求中把請求參數封裝到action、文件上傳和數據驗證等等都是通過攔截器實現的。struts-default定義了這些攔截器和Result類型。可以這麼說:當包繼承了struts-default才能使用struts2提供的核心功能。 struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定義。 struts-default.xml也是Struts2默認配置文件。 Struts2每次都會自動加載 struts-default.xml文件。

包還可以通過abstract=“true”定義爲抽象包,抽象包中不能包含action

例子中使用到的cn.itcast.action.HelloWorldAction類如下

package cn.itcast.action;

public class HelloWorldAction {
	private String message;

	public String getMessage() {
		return message;
	}

	public String execute() {
		message="我的第一個struts2應用";
		return "success";// 返回視圖
	}
}

struts2方法簽名的格式是public String ..返回值必須是string類型.處理完用戶的請求,返回視圖。請求到來之後,交給action處理,執行execute方法。返回視圖
struts.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	<package name="itcast" namespace="/test" extends="struts-default">
        <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >
	<result name="success">/WEB-INF/page/hello.jsp</result>
        </action>
    </package> 
</struts>

 

例子中使用到的/WEB-INF/page/hello.jsp如下:

爲什麼要把jsp放到web-info下面?

因爲只供struts action使用,不希望用戶通過瀏覽器訪問這個jsp,所以放到web-inf,放到外面,用戶訪問是沒有意義的。因爲這個jsp要從action中獲取屬性。必須要經過action才能得到數據。

<%@ page language="java" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <title>第一個struts2應用</title>

  </head>

  <body>

   ${message } <br>

  </body>

</html>

EL表達式訪問的是getMessage,而不是message,如果屬性沒有提供get方法是不行的。

可以使用EL表達式訪問Action中的屬性

 

struts1中,通過<action path=“/test/helloworld”>節點的path屬性指定訪問該actionURL路徑。在struts2中,情況就不是這樣了,訪問struts2actionURL路徑由兩部份組成:包的命名空間+action的名稱,例如訪問本例子HelloWorldActionURL路徑爲:/test/helloworld(注意:完整路徑爲:http://localhost:端口/內容路徑/test/helloworld)。另外我們也可以加上.action後綴訪問此Action。

 <package name="itcast" namespace="/test" extends="struts-default">

        <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >

  <result name="success">/WEB-INF/page/hello.jsp</result>

        </action>

 </package>

3,action搜索順序

1.獲得請求路徑的URI,例如url是:http://server/struts2/path1/path2/path3/test.action

2.首先尋找namespace/path1/path2/path3package,如果不存在這個package則執行步驟3;如果存在這個package,則在這個package中尋找名字爲testaction,當在該package下尋找不到action時就會直接跑到默認namaspacepackage裏面去尋找action(默認的命名空間爲空字符串“” ) ,如果在默認namaspacepackage裏面還尋找不到該action,頁面提示找不到action

3.尋找namespace/path1/path2package,如果不存在這個package,則轉至步驟4;如果存在這個package,則在這個package中尋找名字爲testaction,當在該package中尋找不到action時就會直接跑到默認namaspacepackage裏面去找名字爲testaction,在默認namaspacepackage裏面還尋找不到該action,頁面提示找不到action

4.尋找namespace/path1package,如果不存在這個package則執行步驟5;如果存在這個package,則在這個package中尋找名字爲testaction,當在該package中尋找不到action時就會直接跑到默認namaspacepackage裏面去找名字爲testaction,在默認namaspacepackage裏面還尋找不到該action,頁面提示找不到action

5.尋找namespace/package,如果存在這個package,則在這個package中尋找名字爲testaction,當在package中尋找不到action或者不存在這個package時,都會去默認namaspacepackage裏面尋找action,如果還是找不到,頁面提示找不到action

3,Action配置中的各項默認值

<package name="itcast" namespace="/test" extends="struts-default">

        <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >

  <result name="success">/WEB-INF/page/hello.jsp</result>

        </action>

  </package>

1>如果沒有爲action指定class,默認是ActionSupport

2>如果沒有爲action指定method,默認執行action中的execute()方法。

3>如果沒有指定resultname屬性,默認值爲success

4,Action中result的各種轉發類型

 dispatcher(默認值) 內部請求轉發定向到某一個jsp <result name="success">/WEB-INF/page/hello.jsp</result>

 redirect 重定向某個視圖 如果採用瀏覽器重定向的方式定位到某一個jsp,那麼這個jsp他不能夠放到web-info目錄下,所謂瀏覽器重定向就是引導用戶的瀏覽器訪問某一個路徑,而用戶不能訪問web-inf下的jsp

<action name="redirect">
     <result type="redirect">/employeeAdd.jsp</result>
  </action>

地址欄發生變化

傳遞參數

<action name="list" class="cn.itcast.action.HelloWorldAction"
			method="execute">
			<result name="success" type="redirect">
				/employeeAdd.jsp?username=${username}
			</result>
		</action>
		<action name="redirect">
			<result type="redirect">/employeeAdd.jsp</result>
		</action>


如果傳遞的是中文出現亂碼,怎麼解決呢?

public String execute() throws Exception {
		this.username=URLEncoder.encode("州州", "UTF-8");  //用戶傳遞過來的值,測試固定了
		message="我的第一個struts2應用";
		return "success";// 返回視圖
	}


這時候,打印還是會出現亂碼。傳遞過去的輸入中文的編碼,tomcat接受到中文之後,以iso8859-1存放。必須先得到iso-8859-1的字節數組,變成字符串

 <body>
  ${param.username}
  <%=URLDecoder.decode(new String(request.getParameter("username").getBytes("ISO8859-1"),"UTF-8"),"UTF-8")  %><%-- 這裏是經過url編碼的,所以要解碼--%>
   <form action="/xxx">
   姓名:<input type="text" name="xxx"/>
   </form>
  </body>


redirectAction重定向某一個action

<action name="redirectAction">
    <result type="redirectAction">list</result>
  </action>

如果不在同一個包裏面,該怎麼做呢?

<action name="redirectAction">
			<result type="redirectAction">
			   <param name="actionName">xxx</param>
			   <param name="namespace">/test/department</param>
			</result>
		</action>

 

<package name="other" namespace="/test/department" extends="struts-default">
	   <action name="xxx">
	       <result>/WEB-INF/page/hello.jsp</result>
	   </action>
	</package>

plainText原樣顯示出視圖的代碼 像技術網站,可以瀏覽源代碼

輸出源代碼把整個視圖源代碼顯示到瀏覽器中,沒有執行

<action name="plainText">
    <result type="plainText">/index.jsp</result>
  
  </action>

如果jsp含有中文的情況下會亂碼什麼原因呢?

是因爲文件是utf-8,struts默認讀取會以gbk讀取,所以有亂碼

必須要指定要以utf-8的編碼方式讀取文件

<action name="plainText">
    <result type="plainText">
   <param name="location">/index.jsp</param>
      <param name="charSet">UTF-8</param><!-- 指定讀取文件的編碼 -->

    </result>
   
  </action>

當多個action中都使用到了相同視圖,這時我們應該把result定義爲全局視圖。struts1中提供了全局forward,struts2中也提供了相似功能:

<package ....>

  <global-results>

  <result name="message">/message.jsp</result>

  </global-results>

</package>

<?xml version1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	<package name="itcast" namespace="/test" extends="struts-default">
	<global-results>
	<result name="message">/WEB-INF/page/message.jsp</result>
	</global-results>
	<action name="manage" class="cn.itcast.action.HelloWorldAction"
			method="add">
			
		</action>
		<action name="list" class="cn.itcast.action.HelloWorldAction"
			method="execute">
			<result name="success" type="redirect">
				/employeeAdd.jsp?username=${username}
			</result>
		</action>
		
		<action name="redirect">
			<result type="redirect">/employeeAdd.jsp</result>
		</action>
		<action name="redirectAction">
			<result type="redirectAction">
				<param name="actionName">xxx</param>
				<param name="namespace">/test/department</param>
			</result>
		</action>
		<action name="plainText">
		  <result type="plainText">
		 <param name="location">/index.jsp</param>
	     <param name="charSet">UTF-8</param><!-- 指定讀取文件的編碼 -->

		  </result>
		  
		</action>
	</package>
	<package name="other" namespace="/test/department" extends="struts-default">
		<action name="xxx">
			<result>/WEB-INF/page/hello.jsp</result>
		</action>
	</package>

</struts>

public String add(){
   return "message";
 }

 

而目前的全局視圖在這個包中的。也就是說只有在包中,需要所有包中的action都可以使用呢?

<package name="base" extends="struts-default">
		<global-results>
			<result name="message">/WEB-INF/page/message.jsp
			</result>
		</global-results>
	</package>

 

<package name="itcast" namespace="/test" extends="base">
		<action name="manage" class="cn.itcast.action.HelloWorldAction"
			method="add">

		</action>

5,指定需要Struts 2處理的請求後綴 

前面我們都是默認使用.action後綴訪問Action。其實默認後綴是可以通過常量struts.action.extension進行修改的,例如:我們可以配置Struts 2只處理以.do爲後綴的請求路徑:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <constant name="struts.action.extension" value="do"/>

</struts>

如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。如:

 <constant name="struts.action.extension" value="do,go"/>

 6,細說常量定義

常量可以在struts.xmlstruts.properties中配置,建議在struts.xml中配置,兩種配置方式如下:

struts.xml文件中配置常量

<struts>

    <constant name="struts.action.extension" value="do"/>

</struts>

struts.properties中配置常量

struts.action.extension=do

因爲常量可以在下面多個配置文件中進行定義,所以我們需要了解struts2加載常量的搜索順序:

struts-default.xml

struts-plugin.xml

struts.xml

struts.properties

web.xml  --------   init-param

如果在多個文件中配置了同一個常量則後一個文件中配置的常量值會覆蓋前面文件中配置的常量值.

 

7,常用的常量介紹

<!-- 指定默認編碼集,作用於HttpServletRequestsetCharacterEncoding方法 和freemarker 、velocity的輸出 -->

    <constant name="struts.i18n.encoding" value="UTF-8"/>

    <!-- 該屬性指定需要Struts 2處理的請求後綴,該屬性的默認值是action,即所有匹配*.action的請求都由Struts2處理。

    如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。-->

    <constant name="struts.action.extension" value="do"/>

    <!-- 設置瀏覽器是否緩存靜態內容,默認值爲true(生產環境下使用),開發階段最好關閉-->

    <constant name="struts.serve.static.browserCache" value="false"/>

    <!-- struts的配置文件修改後,系統是否自動重新加載該文件,默認值爲false(生產環境下使用),開發階段最好打開-->

    <constant name="struts.configuration.xml.reload" value="true"/>

    <!-- 開發模式下使用,這樣可以打印出更詳細的錯誤信息-->

    <constant name="struts.devMode" value="true" />

     <!-- 默認的視圖主題-->

    <constant name="struts.ui.theme" value="simple" />

    <!– spring集成時,指定由spring負責action對象的創建-->

    <constant name="struts.objectFactory" value="spring" />

 <!–該屬性設置Struts 2是否支持動態方法調用,該屬性的默認值是true。如果需要關閉動態方法調用,則可設置該屬性爲false-->

<constant name="struts.enable.DynamicMethodInvocation" value="false"/>

 <!--上傳文件的大小限制-->

<constant name="struts.multipart.maxSize" value=“10701096"/>

8,Struts2的處理流程

Struts2的處理流程

StrutsPrepareAndExecuteFilterStruts 2框架的核心控制器,它負責攔截由<url-pattern>/*</url-pattern>指定的所有用戶請求,當用戶請求到達時,該Filter會過濾用戶的請求。默認情況下,如果用戶請求的路徑不帶後綴或者後綴以.action結尾,這時請求將被轉入Struts 2框架處理,否則Struts 2框架將略過該請求的處理。當請求轉入Struts 2框架處理時會先經過一系列的攔截器,然後再到ActionStruts1不同,Struts2對用戶的每一次請求都會創建一個Action,所以Struts2中的Action是線程安全的。

 

9,爲應用指定多個struts配置文

在大部分應用裏,隨着應用規模的增加,系統中Action的數量也會大量增加,導致struts.xml配置文件變得非常臃腫。爲了避免struts.xml文件過於龐大、臃腫,提高struts.xml文件的可讀性,我們可以將一個struts.xml配置文件分解成多個配置文件,然後在struts.xml文件中包含其他配置文件。下面的struts.xml通過<include>元素指定多個配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

  <include file="struts-user.xml"/>

  <include file="struts-order.xml"/>

</struts>

10,動態方法調用

通過這種方式,我們就可以將Struts 2Action按模塊添加在多個配置文件中

動態方法調用如果Action中存在多個方法時,我們可以使用!+方法名調用指定方法。如下:

public class HelloWorldAction{

  private String message;

  ....

  public String execute() throws Exception{

  this.message = "我的第一個struts2應用";

  return "success";

  }

 

  public String other() throws Exception{

  this.message = "第二個方法";

  return "success";

  }

}

假設訪問上面actionURL路徑爲: /struts/test/helloworld.action

要訪問actionother()方法,我們可以這樣調用:

/struts/test/helloworld!other.action

如果不想使用動態方法調用,我們可以通過常量struts.enable.DynamicMethodInvocation關閉動態方法調用。

<constant name="struts.enable.DynamicMethodInvocation" value="false"/>

使用通配符定義action<package name="itcast" namespace="/test" extends="struts-default">

 

11,使用通配符定義action 

 <action name="helloworld_*_*" class="cn.itcast.action.HelloWorldAction" method="{1}

          >

  <result name="success">/WEB-INF/page/{2}.jsp</result>

  </action>

</package>

public class HelloWorldAction{

  private String message;

  ....

  public String execute() throws Exception{

  this.message = "我的第一個struts2應用";

  return "success";

  }

 

  public String other() throws Exception{

  this.message = "第二個方法";

  return "success";

  }

}

要訪問other()方法,可以通過這樣的URL訪問:/test/helloworld_other.action

 

12,接受請求參數

 

採用基本類型接收請求參數(get/post)

Action類中定義與請求參數同名的屬性,struts2便能自動接收請求參數並賦予給同名屬性。

請求路徑: http://localhost:8080/test/view.action?id=78

public class ProductAction {

      private Integer id;

      public void setId(Integer id) {//struts2通過反射技術調用與請求參數同名的屬性的setter方法來獲取請求參數值

             this.id = id;

      }

      public Integer getId() {return id;}

  }

首頁form提交

 <form action="<%=request.getContextPath()%>/test/list_execute_message" method="post">
               編號:<input type="text" name="id"/><br />
                 姓名:<input type="text" name="name"/><br/>
               <input type="submit" value="提交"/>

採用複合類型接收請求參數

請求路徑: http://localhost:8080/test/view.action?product.id=78

 public class ProductAction {

   private Product product;

   public void setProduct(Product product) { this.product = product; }

   public Product getProduct() {return product;}

}

Struts2首先通過反射技術調用Product的默認構造器創建product對象,然後再通過反射技術調用product中與請求參數同名的屬性的setter方法來獲取請求參數值

首頁form提交

 <form action="<%=request.getContextPath()%>/test/list_execute_message" method="post">
               編號:<input type="text" name="person.id"/><br />
                 姓名:<input type="text" name="person.name"/><br/>
               <input type="submit" value="提交"/>
     </form>

action中

private Person person;
    public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}


視圖中

 ${person.id}<br />
 ${person.name }<br />


當Struts2的攔截器接受到了請求參數之後,根據參數名稱,訪問對應的屬性
如果對象爲空,會調用這個對象的默認構造器方法。

如果沒有默認構造器,會抱錯。

關於struts2.1.6接收中文請求參數亂碼問題

struts2.1.6版本中存在一個Bug,即接收到的中文請求參數爲亂碼(post方式提交),原因是struts2.1.6在獲取並使用了請求參數後才調用HttpServletRequestsetCharacterEncoding()方法進行編碼設置 ,導致應用使用的就是亂碼請求參數。這個bugstruts2.1.8中已經被解決,如果你使用的是struts2.1.6,要解決這個問題,你可以這樣做:新建一個Filter,把這個Filter放置在Struts2Filter之前,然後在doFilter()方法裏添加以下代碼

public void doFilter(...){

  HttpServletRequestreq = (HttpServletRequest) request;

  req.setCharacterEncoding("UTF-8");//應根據你使用的編碼替換UTF-8

  filterchain.doFilter(request, response);

}

13自定義類型轉換器

java.util.Date類型的屬性可以接收格式爲2009-07-20的請求參數值。但如果我們需要接收格式爲20091221的請求參數,我們必須定義類型轉換器,否則struts2無法自動完成類型轉換。

import java.util.Date;

public class HelloWorldAction {

  private Date createtime;

  public Date getCreatetime() {

  return createtime;

  }

  public void setCreatetime(Date createtime) {

  this.createtime = createtime;

  }

}

Struts2裏面有個攔截器,一旦檢測出類型轉換出錯。通過el表達式獲取這個值的時候,在棧裏面取得原始的。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

public class DateConverter extends DefaultTypeConverter {

                @Override  public Object convertValue(Map context, Object value, Class toType) {

  SimpleDateFormatdateFormat = new SimpleDateFormat("yyyyMMdd");

  try {

  if(toType == Date.class){//當字符串向Date類型轉換時

  String[] params = (String[]) value;// Request.getParameterValues()

  return dateFormat.parse(params[0]);

  }else if(toType == String.class){//Date轉換成字符串時

  Date date = (Date) value;

  return dateFormat.format(date);

  }

  } catch (ParseException e) {}

  return null;

  }

}

將上面的類型轉換器註冊爲局部類型轉換器

Action類所在的包下放置ActionClassName-conversion.properties文件,ActionClassNameAction的類名,後面的-conversion.properties是固定寫法,對於本例而言,文件的名稱應爲HelloWorldAction-conversion.properties。在properties文件中的內容爲:

屬性名稱=類型轉換器的全類名

對於本例而言,HelloWorldAction-conversion.properties文件中的內容爲:

createtime= cn.itcast.conversion.DateConverter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章