從IO 到框架(6)-Struts2

第一個正式框架Struts2,對"從IO 到框架(4)-Servlet + JDBC (Idea Maven)" 的第二次迭代。

0)三大塊框架


1)Struts2 官方流程圖

ActionMapper (ActionMapping) 負責識別當前的請求需不需要struts2 處理(過濾掉靜態資源);

Interceptors 有18個(在棧中),做表單數據封裝等(代替request.getParameter() );攔截器等組件位於struts-default 包。


2)Hello World, Struts 2 與原生Servlet 的對比:

    1. web.xml 配置:struts2 每個項目只配一個filter,而每個servlet 都需要配一個servlet 及其映射。

    StrutsPrepareAndExecuteFilter 中的init() 方法會解析struts.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>

    2. struts.xml (通過Class.forName() 反射獲得Action 類的Class 對象) 和Action 類(很好地解耦了) 共同完成Servlet 實現類的控制功能。如下圖Struts 2的Facets 沒設置的話,“struts-default" 會報錯。


    配置文件支持動態方法調用和使用通配符(wildcard) 的功能,但不安全,所以不推薦使用。

public class HelloAction {
	public String hello(){
		System.out.println("HelloAction.hello()");
		return "success";
	}
}
<package name="default" namespace="/" extends="struts-default">
        <!-- 默認的action(也需要定義),如果訪問的action找不到就會執行默認的action -->
	<default-action-ref name="defaultAction"/>
        <action name="defaultAction" class="com.qf.action.UserAction" method="defaultAction">
		<result>ok.jsp</result>
	</action>
        <action name="hello" class="com.qf.action.HelloAction" method="hello">
            <result>ok.jsp</result>
        </action>
</package>

    3. Action 類可以實現Action 接口,實現默認的execute() 方法;

        最常用的是繼承ActionSupport類,此類有表單驗證(validate())、國際化(getText())、Token 等功能。返回值用類名點出來,可避免手誤: Action.SUCCESS。

    4. 調用:struts2 只指向方法即可跳轉(簡化事件分發),servlet 需要指明servlet類名以及方法。

    struts2 每次請求都會創建一個新的Action 對象,屬於多例模式;而servlet 是單例模式。

<a href="hello">Hello Struts2</a>
<a href="HelloServlet?method=hello">Hello Servlet</a>

    5. 簡化頁面提交參數獲取 3種方式:

  • 在Action 類中聲明與表單中參數名稱相同的成員變量,併爲其生成getter & setter 方法,即可在Action 類中直接得到頁面參數;而servlet 需要用request 獲取參數。
  • (常用) 參數多時封裝實體類,action中定義對象來接收,表單中寫 "對象.屬性"。
  • (常用) 模型驅動(常用)
    a)實現ModeDriver<Entity>接口,複寫裏面的方法,該方法返回對象
    b)表單中直接寫對象的屬性
    c)原理:調用loign方法之前先調用getMode(),把該方法的返回值壓倒棧頂,然後由攔截器來給棧頂
    的對象賦值。賦值成功後纔會調用login方法

    6. Session 向頁面傳參:

		ActionContext context = ActionContext.getContext();
		Map<String, Object> session = context.getSession();
		session.put("user", user);
		Map<String, Object> request = (Map<String, Object>) context.get("request");
		request.put("msg", "登錄成功!");

        ActionContext 也可獲取原生request:

		HttpServletRequest req = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);

        也可用ServletActionContext 直接獲得Servlet API (常用):

		HttpServletRequest req = ServletActionContext.getRequest();
		req.getSession();
		ServletActionContext.getResponse();
		ActionContext context1 = ServletActionContext.getContext();

        還可用以下實現XxxAware 接口的方式獲得Servlet API:

public class ServletAction extends ActionSupport implements ServletRequestAware,ServletResponseAware,ServletContextAware{
	private HttpServletRequest req;
	private ServletContext servletContext;
	private HttpServletResponse httpServletResponse;

	public String hello(){
		req.setAttribute("msg", "這是requset對象");
		System.out.println("req:"+req);
		System.out.println("servletContext:"+servletContext);
		System.out.println("httpServletResponse:"+httpServletResponse);
		return Action.SUCCESS;
	}
	
	@Override
	public void setServletRequest(HttpServletRequest httpServletRequest) {
		this.req = httpServletRequest;	
	}

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	@Override
	public void setServletResponse(HttpServletResponse httpServletResponse) {
		this.httpServletResponse = httpServletResponse;
	}
}

        對比Servlet:

		HttpSession session = request.getSession();
		session.setAttribute("user", user);
                request.setAttribute("msg", "登錄成功!");

    7. Servlet 需要用reqeust 或response 實現轉發或重定向。

        跳轉的簡化,配置name 默認是"success",默認type 是轉發;

        可通過請求參數動態設置Result,OGNL 通過值棧(數據載體,類似作用域) 取值;

        也可設置全局result, 對這個包下面的所有的action都起作用,先找action下面result,如果找不到就找全局;

        配置Result 中的type - 跳轉(頁面/action) 的方式:

1) dispatcher: 默認值,只能轉發到頁面,不能轉發到action;用到底層的轉發和重定向
2) redirect: 重定向,可以到頁面,也可以到action;用到底層的轉發和重定向
3) chain: 轉發到某個action,不能轉發到頁面;傳遞參數和值struts2 提供
4) redirectAction:只能重定向到action,不能重定向到頁面;不傳遞參數和值struts2 提供
5) stream: 下載的時候使用

3)ActionContext

a)這個對象是ThreadLocal來管理的(用ThreadLocal管理的對象是線程安全的,每個線程都有獨自的一份互不干擾)
b)該對象是個map結構

c)每次請求都會創建一個新的ActionContext

4)ValueStack 值棧
a)可以當成數據載體
b)方法
1)push:壓到棧頂 (如下圖例中將user對象壓到棧頂)
2)set:壓到棧頂的是map
3)peek:查看棧頂,並返回對象
4)pop:彈出棧頂對象並返回
c)值棧對象是放在Request作用域中的,重定向後值棧中的數據就消失了

1)request.getAttribute("struts.valueStack")


5)Struts 標籤和OGNL 配合使用

    1. 標籤
1) UI標籤
2) 非UI標籤
a) 邏輯標籤
b) 迭代
c) 時間
d) 包含
e) 數據展示標籤
    2. OGNL
1) 對象導航語言,工作在視圖層,用來取代頁面中的java 腳本,簡化數據的訪問操作。
2) 符號
a) #: 訪問非根元素的時候需要加#
b) %: struts2標籤不認識OGNL表達式的時候需要用%包起來
c) $: 動態參數,可以配置文件訪問值棧中的內容
3) 在使用的時候一般都是stuts2的標籤和OGNL結合使用
4) 用OGNL訪問域對象中的數據
a)#request.msg --> ${requestScope.msg};

b)#attr.msg -- > ${msg}

注: attr 用於按request > session > application順序訪問其屬性(attribute)
        #attr.userName相當於按順序在以上三個範圍(scope)內讀取userName屬性,直到找到爲止。

6)類型轉換器 typeConverter
a)stuts2基本類型都會自動轉,不需要我們處理
b)自定類對象類型的時候就需要到類型轉換器
1)先定義一個類型轉換器
a)繼承StrutsTypeConverter
1)從字符串轉成對象,表單中name要寫對象的名稱
2)從對象轉成字符串,獲取也要對象的名稱
b)配置類型轉換器
1)全局
1)位置:放在src下面
2)命名:xowrk-conversion.properties
3)內容:對象的全類名=類型轉換器的全類名
2)局部
1)位置:和actino放在一起
2)命名:ActionName-conversion.properties
3)內容:對象=類型轉換器的全類名
c)自定義類型轉換器要和struts2標籤一起使用


7)用編程校驗(儘量放在前臺校驗,減少服務器壓力)

1)Action要繼承ActionSupport
2)校驗失敗後默認的返回值input,一般都是跳轉到原頁面
3)如何校驗
a)複寫父類中的validate方法
1)這個方法對所有的方法都起作用
b)validateXxxx(validateAddUser)
1)只對addUser方法起作用
c)錯誤的添加
1)調用父類中addFieldError("屬性名","錯誤信息")
    用配置文件校驗
1)配置文件
1)命名:<ActionClassName>-<aliasName>-validation.xml
a)aliasName是action名字不是方法的名字
2)位置:和action放在一起
3)內容
1)約束文件
2)校驗器(struts2提供的)
a)xwork-core.jar/com.opensymphony.xwork2.validator.validators/default.xml
2)校驗失敗後默認的返回值input,一般都是跳轉到原頁面

8)攔截器 
1)概念
a) 是struts2提供的一個組件
b) 攔截器和servlet沒有關係
c) 攔截器只攔截action
2)如何定義攔截器
a) 寫一個類繼承AbstractInterceptor, 複寫裏面的方法
3)配置
1) <interceptors>
a) <interceptor>
1)name:攔截器的名字
2)class:攔截器的全類名
b) <interceptor-stack>
1)name:攔截器棧的名字
2)<interceptor-ref>:可以引入攔截器,也可以引入攔截器棧
2) <default-interceptor-ref>
a) 引入要執行的攔截器(棧)
4) 應用:記錄Action執行時間,用到AOP 思想;登錄攔截。

9)國際化

    1. Java 國際化

1) 簡稱i18n
2) Locale:本地化
a) zn_CN
b) en_US
3) ResourceBundle:可以通過這個類來實現國際化
a) 資源文件
1) 命名:resource_zn_CN.properties
2) 位置:放在src下面
4) MessageFormat:可以設置動態參數
    2. struts2中的國際化
1. 配置baseName <constant name="struts.custom.i18n.resources" value="resource"></constant>
2. 繼承ActionSupport,否則無法調用父類中的getText()
3. 頁面國際化 -兩種方式
1) label:通過ONGL表達式 %{getText('username')} 調用ActinoSupport類中的getText()
2) key:直接寫資源文件中的key,代替label
4. 後臺提示信息國際化
1) 調用父類中的getText("key")

10)上傳

    1.文件上傳

1.表單中要修改enctype屬性 enctype="multipart/form-data"
2.action中定義三個屬性
1)myFile:上傳的文件對象(臨時)
2)myFileFileName(上傳的文件名稱)
3)myFileContentType(上傳的文件類型)
3. action 方法中先上傳到一個臨時文件,再拷貝
    2.校驗 -以下兩項都需要
1.文件上傳的攔截器(fileUpload),在action 裏設置
1)文件類型 
a)去tomcat的web.xml裏面去找,賦值mimeType過來
b) 可國際化攔截的提示信息,覆蓋原英文提示信息。
Idea 雙shift(右上角放大鏡) 查找到struts-messages.properties, 在中文資源文件重寫。
2)文件大小
2.引入默認的攔截器棧 (給action中的屬性賦值是由默認攔截器做的)
    3.批量上傳
1.表單裏面定義多個上傳控件
2.action中的接收成員變量都換成數組,記得setter getter

11)文件下載 -兩種方式差不多
1. 普通流程
a)先準備被下載下載的文件
b)把文件轉成流(ips)
c)設置響應頭 -注意只能響應一次,再return "success" 的話會報錯;這裏也瞭解了Action 裏的方法
是可以返回void的,相應地struts.xml 可以沒有結果集。
1)處理文件中文的問題 fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
或者 fileName = URLEncoder.encode(fileName, "utf-8");

response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

d) 字節IO流的拷貝
2.struts2中的文件下載
1)把文件流和文件名稱設置到值棧中,即設爲成員變量
2)<result>中的type屬性配置的是stream,result 下級param節點設置以下兩個參數
a)設置流的名稱

b)設置響應頭

12)ajax和Action交互
解決亂碼:response.setContentType("text/html;charset=utf8");
1.導入json的插件包 struts2-json-plugin
2.包要繼承json-default
3.把對象裝到集合裏面,把集合放入的值棧中,setter getter

4.返回類型是json

1)需要指定要裝換的數據
<param name="root">userList</param>
瀏覽器打開開發者模式,可以在js(jQuery) 中加斷點"debugger" 來調試

13)token
1)能解決表單重複提交的問題
不允許提交後 後退,原封不動再提交,但後退刷新再填再提交是允許的。
2)如何使用
a)表單中添加token標籤
b)再添加一個攔截器,並加上攔截器棧 "defaultStack"
c)如果驗證到是重複提交默認的返回值invalid.token
3)原理
1)先到服務端獲取key(key也在服務端有一份)
2)表單的提交的時候先判斷key是否有效
a)如果有效就執行action,刪除key

b)如果無效就說明已經是重複提交

14)全局的異常處理 -很實用但實際生產用Spring,機制類似
1. 在package 裏面配:<global-exception-mapping>
<exception-mapping> 兩個屬性
a)result:result的name
b)exception:異常的全類名
2.捕獲機制
1)先找當前的異常,如果找到了就跳轉到對應的頁面,如果沒有配置就找該異常的父類,跳轉到對應的頁面
如果父類沒有找到就報異常信息暴露給用戶
15)模塊化開發,struts配置文件分成多個模塊,再包含進總配置文件
<include file="user.xml"></include>








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