struts2-第一天
框架搭建
1.jar包的準備
2.配置web.xml,配置struts2核心控制器,接收所有的請求(可在dispatcher.ng.filter包下拷貝,把.class去掉)。
3.拷貝struts.xml和log4j到src目錄下。
3.1寫個package包,給個名字:user,包就相當於一個模塊,再添加extends="struts-default"
3.2裏面寫action標籤,寫name和實體類
3.3裏面寫result標籤,寫name和頁面
執行的過程:請求url滿足了/*,就交給了核心控制器,它啓動會把struts.xml文件的配置全部讀到內存中,用一個對象來保存,會在這<action>裏面找有沒有login的配置,接着會找到對應的類,當action配置裏沒有配method屬性,它就執行業務邏輯控制器類裏的execute(),方法完成後會返回字符串,這個字符串會交給核心控制器,再找<result>裏對應的字符串,匹配就會用這個頁面生成響應到客戶端。
struts2-core-2.3.15.1.jar裏org/apaqi/struts有配置文件default.properties,
1.更改請求後綴<constant name="struts.action.extension" value="do">
2.開啓開發模式<constant name="struts.devMode" value="true">
ActionSupport類
struts.xml裏,<package>配置extends="struts-default"的好處是,如果action裏沒有配置class屬性,將使用struts-default.xml(在覈心jar包裏)中配置的默認類com.opensymphony.xwork2.ActionSupport(最後面),如果沒有配置method,就使用execute(),如果result裏沒有配置name,默認是success
請求參數接收
一、標量值方式接收參數
struts對於提交到action的參數,會當作這個action有這個屬性,就會調用這個參數set方法來接收(保存)提交的參數。
二、複合類型方式接收參數(常用)
三、模型驅動方式獲取參數(少用)
1.業務邏輯控制器類要實現ModelDriven<?>接口
2.預先創建user,並提供方法返回user
3.代碼不需要寫getset方法
四、servlet API方式獲取參數(需掌握)
1.業務邏輯控制器類要繼承ActionSupport
2.通過ServletActionContext.getRequest()得到HttpServletRequest
3.就可以用以前getParameter()
Servlet APi 獲取
一、ServletActionContext類(重點)
ServletActionContext.getRequest().getSession()
二、Struts2Aware攔截器IOC模式(重點)
1.實現ServletRequestAware接口,重寫setServletRequest(HttpServletRequest request),把參數賦值給成員變量HttpServletRequest request,後面就可以使用request。
三、四、ActionContext類的get方法
Action動態方法
需要開啓動態方法調用支持,<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
一、login!login.do歎號方式.注意:用感嘆號動態調用時,配置文件的action是不寫!的,在提交地址那裏寫,如不寫!指定調用的方法,默認調用execute方法。
二、login_*統配
命名空間
1.命名空間前面加/
規則是:1.1如果命名空間存在,進入命名空間搜索action,action存在就執行,不存在就報異常
1.2如果命名空間不存在,自動刪除最後的命名空間,再進行搜索
2.加命名空間後,提交地址要加前面那段(不能/開頭),設置了後綴加後綴,但配置那裏的action的name屬性不加後綴(這裏必須注意)。
3.當配置文件struts.xml的<package/>用了命名空間時,響應頁面一般要加/。而提交地址要加命名空間.
六、類型轉換
2013-1-15默認會轉換?月份有0嗎?
自定義類型轉換器的實現
方式1.繼承DefaultTypeConverter類
1.1如上,UserAction類要繼承ActionSupport類(如不繼承,無法顯示異常等)
1.2重寫convertValue方法
1.3在action包下新建一個同名的UserAction-conversion.properties,文件內容:user.loginTime屬性 = org.fkjava.converter.MyConverter轉換器
全局的跟struts.xml配置文件放在一起
方式2.繼承StrutsTypeConverter類(抽象類)(推薦使用)
--有兩個要實現的方法,主要實現前臺到後臺convertFromString(Map context,String[] values,Class toType)方法,第一個參數是上下文,第二個是提交的實際數據,第三個是提交數據的類型
方法實現邏輯:判斷values不爲空後,如果寫的是轉換時間的轉換器,判斷toType==Date.class,一旦涉及到時間對象,simpleDateFormat,從字符串轉換爲Date類型用sdf.parse(values[0])
全局:在src下xwork-conversion.properties,java類型=自定義轉換器
轉換錯誤顯示:
1.默認:
1.1<%@taglib prefix="s" uri="/struts-tags"%>,<s:fielderror>
2.局部類型轉換失敗自定義信息
2.1在action包下新建一個UserAction.properties文件,內容是invalid.fieldvalue.屬性名(表單提交的name屬性user.loginTime)=提示語句
2.2throw new TypeConstraintException("日期類型轉換失敗!");
3.全局類型轉換失敗自定義信息
3.1 src根目錄下,fkjava.properties國際化資源文件:xwork.default.invalid.fieldvalue={0},提示信息
3.2 struts.xml下配置常量name="struts.custom.i18n.resources" value="fkjava"/>
七、後臺驗證
如果希望使用struts2提供的驗證框架,必須extends ActionSupport
調用步驟:
1.先類型轉換
2.驗證
3.調用目標action方法
方式一:代碼方式實現輸入驗證
a.對action所有方法進行驗證
方式1:重寫ActionSupport中的validate方法
//針對本action的所有action方法進行驗證
public void validate(){
System.out.println("-------validate()--------");
if(user == null || "".equals(user.getUserName())){
/**一旦addFieldError()加了東西,就會返回INPUT*/
this.addFieldError("user.userName","請輸入用戶名");
}
}
--輸出錯誤信息:<s:fielderror/>
b.對單個方法進行驗證,只需要在這個方法的後面加你要驗證的方法,如login方法要驗證,validateLogin()
方式二:配置xml方式實現輸入驗證(比較常用)
xml文件名寫法:NoticeAction-addNotice-validation.xml針對一個action請求做後臺驗證(中間是<action name=""/>裏name的值)
NoticeAction-validation.xml針對整個NoticeAction處理類都做後臺驗證
例子:
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<field name="user.userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>請輸入用戶名3!</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">6</param>
<message>請輸入${minLength}~${maxLength}範圍的用戶名3</message>
</field-validator>
</field>
</validators>
國際化
表單:
1.寫fkjava.properties國際化文件,依次再寫其他語言的properties。說明:裏面寫的就是需要替換的字眼
2.用struts2標籤庫寫表單(先引入),表單裏的每一項多一項key的屬性,寫的就是properties裏面的key
3.如果想動態改變語言環境,加一個帶request_locale參數的表單,如直接提交到jsp不行,就配置一個最簡單的action把表單提交到action,再<result>到jsp
頁面:
1.使用i18n標籤,name=資源文件前綴,<s:text name=資源文件中的key>,再裏層標籤<s:param>${username}</s:param>
<s:i18n name="fkjava">
<s:text name="welcome">
<s:param>${user.userName}</s:param>
</s:text>
</s:i18n>
代碼中:
1.put之後,利用ActionContext.getContext.put("msgs",this.getText("國際化資源裏的key")),頁面上取出:${msgs}
result的type屬性
服務器重定向
1.dispatcher(默認),轉到資源(jsp html 其他視圖技術)
2.chain,轉到一個action,參數有actionName,nameSpace,method等。
客戶端重定向(參數不會自動傳遞,如需傳遞,在actionName拼接,或加<param name="user.userPass">123456</param>)
動態跳轉:定義動態結果頁面(如果登錄名等於tom,跳到tom.html)定義成員變量typePath,並提供getset方法
全局result
1.包裏配置<global-results>。當局部沒有找到返回的字符串時,會去找全局,如果全局也找不到就生成默認錯誤響應。
2.如果局部全局都有,局部起作用。
異常處理
1.局部<exception-mapping result="result字符串" exception="java異常"/>
2.全局<global-exception-mappings>
<exception-mapping result="" exception=""/>
注意:當局部異常處理找不到纔會找全局
3.異常信息顯示
方式一:引用struts標籤庫,<s:property value="exception.message">和<s:property value="exceptionStack">
方式二:直接EL表達式:${exception.message}和${exceptionStack}
攔截器
自定義攔截器
1.寫一個類繼承AbstractInterceptor,重寫intercept方法。
2.invocation.invoke()把控制權限交給下一個攔截器,沒有的話交給action,返回值String就是提交給action執行的方法執行完以後返回的字符串
3.配置攔截器在package裏,引用(使用)是在action裏。
4.一旦自定了攔截器,默認的就不起作用。
5.攔截器的定義順序決定了調用的順序,最好先配置好默認的攔截器
6.如果有一堆的攔截器要用,可以配置一個攔截器棧
7.如果在一個package中不想每一個action都寫引用攔截器,可以在package裏定義默認攔截器<default-interceptor-ref name=""/>(本包有效)
方法攔截器
1.寫一個類繼承MethodFilterIntercepter,重寫doIntercept方法。
2.引用時多一項參數<param name="includeMethods">login,logout</param>,指定哪些會調用攔截器(白名單)
3.如果白名單和黑名單衝突,白取勝(邪不壓正)。
內置攔截器
timer:統計action方法執行耗時的攔截器
token:防止表單重複提交
1.在表單提交的action上調用默認攔截器<interceptor-ref name="token"/>
2.配置當出現重複提交時到什麼頁面<result name="invalid.token">error.html</result>
3.頁面上用struts標籤庫,在form里加上<s:token/>,其他不變
登錄檢查
1.自定義一個攔截器
String result = "login";
if(flag){
HttpSession session = ServletActionContext.getRequest().getSession();
User u = (User)session.getAttribute("loginUser");
if(u != null){
result = invocation.invoke();
}
}else{
result = invocation.invoke();
}
return result;
2.配置(聲明)攔截器
3.調用攔截器(先調用系統默認),裏面還可以給攔截器參數(攔截器的成員變量要有getset方法)
freemarker
使用要引入一個jar包和要在web.xml文件中做一個配置。
文件上傳下載(注意這裏:配置跟default.properties有關!)開始是在struts2-core-2.3.15.1.jar這個jar文件裏的
一、單個文件上傳
上傳的form表單的method屬性必須是post,enctype必須是multipart/form-data
1.頁面的表單<form action="upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="headImg">
<input type="submit" value="上傳"/>
</form>
這時要配置action:
<package name="user" extends="struts-default" >
<action name="upload" class="org.fkit.action.UserAction" method="upload">
<result name="upload">success.jsp</result>
<result name="input">error.jsp</result>
</action>
</package>
注意這裏action配置沒有命名空間,如果有,表單提交action要加命名空間。??
2.UserAction裏
2.1增加屬性
private File headImg;//接收文件用的屬性(可以跟表單的不一致,因調的是set方法)
private String headImgFileName;
private String headImgContentType;
//三個屬性要增加getset方法
說明:這樣寫才能獲取到文件名和文件類型
2.2.upload方法裏
//獲取服務器的資源(存儲)路徑
String path = ServletActionContext.getServletContext().getRealPath("images");
File file = new File(path,headImgFileName);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileUtils.copyFile(headImg, file);
2.3如想方便查看上傳的文件,可設置上傳路徑:雙擊tomcat,在ServerLocations那裏,
選中間那一欄(使用tomcat安裝路徑)。
二、多個文件上傳
屬性改爲數組,循環new File,其他基本不用變。
上傳約束配置
1.全局大小限制:struts.xml裏,配置整個工程:<constant name="struts.multipart.maxSize" value="數值">
2.局部大小限制:局部的大小限制必須小於全局大小限制!在對應的action下配置<interceptor-ref name="fileUpload"/>攔截器,攔截器裏可以設置參數:
<param name="maximumSize">數值</param>
注意點:建議把默認的攔截器放在上傳約束攔截器下面。
3.類型約束:<param name="allowedTypes">image/pjpeg,image/x-png</param>
4.後綴約束:<param name="allowedExtensions">.jpg,.png,.bmp</param>
三、下載
1.struts-user.xml文件配置下載的action:
<package name="user" namespace="/" extends="struts-default">
<action name="download" class="org.fkjava.action.UserAction">//沒配method默認執行execute方法
<result name="success" type="stream">
<param name="contentType">application/octet-stream</param>
<param name="inputName">fileStream(要與Action類getset方法對應)</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>//對應action類fileName屬性get方法
</result>
</action>
</package>
2.在UserAction類增加getFileStream方法
public InputStream getFileStream(){
return ServletActionContext.getServletContext().getResourceAsStream("/images/"+fileName);
}
還要增加fileName屬性,提供getset方法,在頁面上增加這個參數。
3.中文亂碼:在取流的方法裏,return之前加上這句(getResource裏的變量毫無疑問改成s):
String s = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
4.下載頁面:
<a href="download.do?fileName=mm.jpg">下載1</a>
OGNL表達式
<s:debug/>標籤:用來調試程序
值棧(valueStack):取值棧裏的值:${user.userName}或者<s:property value="user.userName"/>
棧(stack)中獲取數據要加#號:<s:property value="#request.r"/>或者${requestScope.r}或者${r}(先找小範圍,再找大範圍,如果小範圍找到,不會再找大範圍)
訪問對象屬性中還是對象:<s:property value="user.course.name"/>即:對象.對象.屬性
訪問方法:調用值棧中對象的普通方法:<s:property value="user.sayHello()"/>
開啓允許靜態方法調用:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
取值棧的一個例子:
<td><s:radio theme="simple" list="#{'1':'男','2':'女'}" id="sex" name="vo.sex" value="%{#request.vo.sex}"></s:radio></td>
Struts2標籤
1.控制標籤
1.1-iterator標籤操作Map:
方式一:操作簡單的Map:
value是json數據,var是臨時變量,顯示用<s:property value="#var裏的變量.key">,<s:property value="#var裏的變量.value">
方式二:後臺準備數據,
Map<String, Object> request = ac.getContextMap();
Map<String, String> maps = new HashMap<>();
maps.put("A","中國");
maps.put("B","美國");
maps.put("C","英國");
request.put("maps",maps);
頁面:value="#request.maps",其他不變
1.2—iterator標籤操作List:
方式一:這時value不用#號{'a','b','c'},顯示就去掉.key和.value,其他一樣。
方式二:List<User> userList = new ArrayList<>();
userList.add(new User("admin",12,new Date()));
userList.add(new User("admin2",13,new Date()));
request.put("userList",userList);
頁面:value="#request.userList" var="l",顯示<s:property value="#l.userName"/>
iterator標籤進行數據過濾操作
?獲取滿足條件的所有數據
value="#request.userList.{?#this.userPass %2 != 0}"//意思是判斷當前迭代對象的userPass與2取餘不爲0就輸出
^獲取滿足條件的第一條數據
value="#request.userList.{^#this.userPass %2 != 0}"
$獲取滿足條件的最後一條數據
value="#request.userList.{$#this.userPass %2 != 0}"
if...else if...else
<s:if test="attr.number % 7 == 0">
輸出語句
</s:if>
<s:elseif test="attr.number % 5 == 0">
輸出語句
</s:elseif>
<s:else>
輸出語句
</s:else>
append集合合併標籤:把兩個list拼接在一起放在newList裏
<s:append var="newList">
<s:param value="{1,2,3}"/>
<s:param value="#userList"/>
</s:append>
<s:iterator value="#newList" var="nl">
<s:property value="#nl"/><br>
</s:iterator>
generator字符串分割標籤:分割完是一個數組g
<s:generator separator="," val="'a,b,c'" var="g">
<s:iterator value="#g" var="nl">
<s:property value="#nl"/><br>
</s:iterator>
subset獲取子集合
方式一:在裏面迭代
<s:subset source="{1,2,3,4,5}" start="2" count="2">
<s:iterator var="s">
<s:property value="#s"/><br>
</s:iterator>
</s:subset>
方式二:在其他地方迭代
<s:subset source="{1,2,3,4,5}" start="2" count="2" var="subs"/>
<s:iterator value="#attr.subs" var="s">
<s:property value="#s"/><br>
</s:iterator>
2.數據標籤
set標籤:指定把某值用var的變量名字設置到某個範圍
<s:set value="要存的值" var="myVar" scope="session"/>
<s:property value="#session.myVar"/>
push標籤:將某個值置於值棧的頂部,標籤結束後,將從值棧中移除。
<s:push value="'push中的數據'">
<s:property/>
</s:push>
bean標籤:可以在頁面創建一個對象
<s:bean name="org.fkit.bean.User" var="u">
<s:param name="userName">tom</s:param>
<s:param name="userPass">123456</s:param>
</s:bean>
<s:property value="#u.userName"/>--<s:property value="#u.userPass"/>
date標籤:
<%
request.setAttribute("now",new java.util.Date());
%>
<s:date name="#request.now" format="yyyy年MM月dd日HH時mm分ss秒"/>
include標籤
================================
4、struts2的處理流程
a、browser-->struts2控制器-->查找action(在配置文件找)-->(回到)struts2控制器-->攔截器1,2,3...-->動作Action-->(request)struts2控制器-->查找視圖(配置文件找)-->struts2控制器-->視圖-->browser
一個請求在Struts2框架中的處理大概分爲以下幾個步驟
1 客戶端初始化一個指向Servlet容器(例如Tomcat)的請求
2 這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對於Struts2和其他框架的集成很有幫助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被調用,FilterDispatcher詢問ActionMapper來決定這個請是否需要調用某個Action
4 如果ActionMapper決定需要調用某個Action,FilterDispatcher把請求的處理交給ActionProxy
5 ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調用的Action類
6 ActionProxy創建一個ActionInvocation的實例。
7 ActionInvocation實例使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。
8 一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可 能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2 框架中繼承的標籤。在這個過程中需要涉及到ActionMapper
在上述過程中所有的對象(Action,Results,Interceptors,等)都是通過ObjectFactory來創建的。
5、Struts2配置文件
a、可以給每個模塊定義一個包(Package元素的namesapce屬性)
b、繼承某個package(Package元素的extends屬性)
c、爲了讓大型應用的struts.xml更好管理,可以把它分成幾個較小的文件ps:
<include file="user.xml"></include>
6、攔截器是什麼?struts-default.xml
是將控制器不做的事情封裝在另外一個地方
a標籤路徑最好是通過絕對路徑來:request.getContextPath()
接收參數三種方式:
1.Action屬性來接收
2.用VO來封裝表單屬性
3.利用ModelDriven模型驅動來接收
要實現 implements ModelDriven<T>接口,Action裏面重寫getModel()方法。
==========填充下拉列表==========
第一種:
<s:select list="#{1:'男',2:'女'}" name="employee.sex" headerKey="0" headerValue="--請選擇性別--"></s:select>
第二種:
先定義private List<Map<String,Object>> jobs;
<s:select list="jobs" name="employee.job.id" headerKey="0" headerValue="--請選擇職位--" listKey="id" listValue="name"></s:select>
第三種:
$.ajax({
url : "${ctx}/dept/loadDeptAjax.action",
type:"post",
dataType:"json",
async : true,
success:function(data){
//獲取選中的
var deptId = "${employee.dept.id}";
alert();
$.each(data,function(){
$("<option/>").val(this.id).text(this.name).attr("selected",this.id == deptId).appendTo("#deptSelect");
});
}
});
List<Map<String,Object>>dept = deptMapper.find(null);
========================Struts2========================
namespace
1、命名空間一般是以“/”開頭
2.當namespace=""時,它會匹配所有的。
request.getScheme()+request.getServletName()+request.getServletPort()+path;
動態方法調用(DMI):
意思就是不配置method屬性,在請求的時候這樣:user!add
<action name="student*" class="xx" method="{1}">
<result >/student{1}_success.jsp</result>
</action>
<action name="*_*" class="{1}Action" method="{2}">
<result >/{1}_{2}_success.jsp</result>
</action>
參數接收:
1.普通類型
2.用DTO來接收
public class UserDTO{
private String name;
private String pwd;
private String cpwd;
}
3.模型驅動:
public class UserAction implements ModelDriven<User>{
private User user = new User();
public String add(){
return SUCCESS;
}
public User getModel(){
return user;
}
}
參數校驗:
addFieldError("name","name is error!");
頁面:
<s:fielderror fieldName="name" theme="simple"/>
(Map)ActionContext.getContext().get("request");
session = ActionContext.getContext().getSession();
在頁面上拿,要用#key這種方式。
Result的配置
<result type="chain">
forward到另外一個action
全局result
<global-results>
<result name ="mainpage">/main.jsp</result>
</global-results>
動態結果集
<result>${r}</result>
遍歷操作:
<s:iterator value="{1,2,3}">
</s:iterator>
<s:iterator value="{'aaa','bbb','ccc'}" var="x">
<s:property value="#x.toUpperCase()"> |
</s:iterator>
<s:iterator value="{'aaa','bbb','ccc'}" status="status">
<s:property /> |
遍歷過的總數:<s:property value="#status.count" />
遍歷的元素索引<s:property value="#status.index" />
當前是偶數:<s:property value="#status.even" />
當前是奇數:<s:property value="#status.odd" />
是第一個元素嗎:<s:property value="#status.first" />
是最後一個元素嗎:<s:property value="#status.last" />
</s:iterator>
遍歷map
<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" >
<s:property value="key"> | <s:property value="value">
</s:iterator>
<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" var="x">
<s:property value="#x.key"> | <s:property value="#x.value"/>
</s:iterator>
struts2錯誤標籤自動有樣式:<s:fielderror /> errorMessage
可以.fielderror ul li{ list-style-type:none; }解決
struts2Filter -> 攔截器 -> xxAction
↓
struts2Filter <- 攔截器 <- xxAction
默認攔截器都配置在struts-default.xml
攔截器也是配置在struts.xml文件裏,
<package name="webpub" extends="struts-default" namespace="/">
<interceptors>
<!-- 配置攔截器與攔截器棧 -->
<interceptor name="" class=""></interceptor>
<interceptor-stack name="customStack">
<interceptor-ref name="exceptionFilter"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="authority"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 配置默認的攔截器(攔截器棧) -->
<default-interceptor-ref name="customStack"></default-interceptor-ref>
<!-- 當我們在配置Action的時候,如果沒有爲某個Action指定具體的class值時,系統將自動引用<default-class-ref>標籤中所指定的類。在Struts2框架中,系統默認的class爲ActionSupport,該配置我們可以在xwork的核心包下的xwork-default.xml文件中找到。有特殊需要時,可以手動指定默認的class -->
<default-class-ref class=""></default-class-ref>
<!-- 當配置裏的action都對應不上的時候,最後就會執行這個 -->
<default-action-ref name=""></default-action-ref>
</package>