TA的每日心情 | 衰 2021-2-2 11:21 |
|---|
签到天数: 36 天 [LV.5]常住居民I
|
9#
发表于 2015-06-02 12:45:13
|只看该作者
三、 Struts2% |/ z/ u- y0 m- {: {
1. Action名称的搜索顺序
: m. P' w5 [2 V1 a0 n(1).获取请求路径的URL,例如URL是:http://server/struts2/path1/path2/path3/test.action4 ~" k8 o2 B4 N; P
4 N1 L- E1 `2 Z) c
(2).首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步棸3,如果存在这个package,则在这个package中寻找名字为test的action,当在该package下寻找不到action时就会直接跑到默认namespace的package里面中寻找action(默认的命名空间为空字符串),如果在默认namespace的package里面还寻找不到该action,页面提示找不到action
% S. M( j8 t+ Z) K$ } . t+ ^. r1 [% `% {( h2 d) a
(3).寻找namespace为/path1/path2的package,如果不存在这个package,则转至步棸4,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认的namespace的package里面还寻找不到该action,页面提示找不到action
+ c! L& F. B! v4 @0 a; a2 n( O7 e " x! ?3 q, I; U0 i g6 [
(4).寻找namespace为/path1的package,如果不存在这个package则执行步棸5,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认namespace的package里面还寻找不到该action,页面提示找不到action% O, n& X" C2 c z3 X
6 \9 a, O& W8 Q9 L( f0 K1 g
(5).寻找namespace为"/"的package,如果存在这个package,则在这个package中寻找名字为test的action,当在package中寻找不到action或者不存在这个package时,都会去默认namespace的package里面寻找action,如果还是找不到,页面提示找不到action
: j$ u ~0 j9 P7 F5 ]
; z" I2 p: ^' e7 y3 [* j(6).在struts2中默认的处理后缀是.action,不加后缀也可以) ^4 O+ L2 T1 y
9 D; O- X, C8 f6 p2. Action配置的各项默认值+ x- ~" A0 a# x4 o; X% }
(1).struts1中:<actionpath="/control/employee/addUI"forward="/WEB-INF/page/employeeAdd.jsp"/>实现请求转发,action将请求转发给视图jsp& ]* c& d& i. E& X- b& D5 l8 r/ {
; r j' t9 ]: I" p0 K
(2).在struts2中,<actionname="addUI"><result>/WEB-INF/page/employeeAdd.jsp</result></action>,不需要设置addUI的类路径class属性了
7 ?: @/ e1 V/ [/ w! Y + p H1 i8 |3 E3 c2 b
(3).Action配置中的各项默认值:+ W3 f' t, S- \( _8 a, ?
<package name="itcastnamespace="/test" extends="struts-default">
+ V% ~/ Q' s5 `; A<action name="hellowrold"class="cn.itcast.action.HelloWorldAction"method="execute">' A j$ a' v2 t0 R/ w$ t
<result name="success">/WEB-INF/page/hello.jsp</result> Z1 c9 P! ]! U3 {+ A5 q5 @
</action># e+ t1 e7 [& f9 z* F! }
</package>
# V9 c4 g& U8 A( b" W如果没有为action指定class,默认是ActionSupport,可以查看ActionSupport的源代码,首先交给ActionSupport类处理." L; F4 Q) e- l# F! C/ u4 O
如果没有为action指定method,默认执行action中的execute()方法,这个方法的返回值为"success";- v8 }6 [2 ]" L
! G1 w/ K& B1 x) H
(4).如果没有指定result的name属性,默认值为success,正好和execute方法的返回值相同,所以可以实现视图的转发. X7 X7 F3 G% B
1 H5 ^. S) z. z2 O3.OGNL表达式9 B* J, {8 W: U5 e
(1).OGNL表达式:OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目,struts2框架使用OGNL作为默认的表达式语言# }/ o _9 A0 W8 R- N
第一:相对EL表达式,它提供了平时我们需要的一些功能,如:3 B# j$ S1 I. ~/ l6 P& z2 c# R
支持对象方法的调用,如:xxx.sayHello();0 P- F. k; g0 r. J
第二:支持类静态方法调用和值访问,表达式的格式为@[类全名 (包括包路径) ]@[方法名|值名],例如:@java.lang.String@format('foo %s,'bar')或@cn.itcast.Constant@APP_NAME
) l8 d7 C; b2 o% X第三:操作集合对象
& y$ n6 \/ T. o9 y, P9 F9 u
2 v# c* A6 l( l7 k8 b' z2 B5 X(2).OGNL有一个上下文(Context)概念,说白了上下文就是一个MAP结构,他实现了java.utils.Map接口,在struts2中上下文(context)的实现为ActionContext
- S+ H+ h3 E- Z. l! i! ? ; K4 | y. I Q/ u& \1 a
(3).struts2中的OGNLContext是实现者为ActionContext,它的结构为:OGNL Context:ValueStack(值栈,他是根对象),parameters,request,session,application,attr,当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action,然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问5 y+ G5 W# n+ q9 F
. ^' G. I6 _$ |/ ?0 U8 K(4).当要访问某个对象只需在其前面加上一个'#',例如:#request,当然有一个特殊的例子,就是根对象,会省略'#',OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈),如果要访问根对象(即ValueStack)中的对象的属性,则可以省略#命名空间,直接访问该对象的属性即可.& P* d" e( w/ Y6 J
3 H' J) {+ F3 D h! d0 _(5).在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的root变量,就是使用它存在一组对象,在root变量中处于第一位的对象叫栈定对象(存放action),通常我们在OGNL表达式里直接写上属性的名称即可访问root变量对象的属性,搜索顺序是从栈定对象开始寻找,如果栈定对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。大家注意:struts2中,OGNL表达式需要配合struts标签才可以使用,如:<s:property value="name"/>,value属性接受的是OGNL表达式,搜索是否含有name属性
" C0 B3 }8 e. i % y7 B7 \' D9 z0 K, K: B
(6).由于ValueStack(值栈)是struts2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性:
- ?- Q& K% u, Y- B) t' Q${foo}获得值栈中某个对象的foo属性
% S; @/ `$ M- A% m' f如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀1 D& K# B# _0 J/ A6 w2 }
第一:application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username");! ?& E3 f6 m+ w. P, G. k# P
第二:session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName");
# O" a/ c) L! ~5 c6 R5 R第三:request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribut("userName");, F V: B2 I% x$ s3 u8 `. @9 K
第四:parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username");
; T! x. D: W$ Z7 D" s第五:attr对象:用于按page->request->session->application顺序访问其属性.
5 R* r7 y3 W1 v/ r+ C4 K , u/ {# N) y2 F }
(7).为何使用EL表达式能够访问valueStack中对象的属性:原因是struts2对HttpServletRequest做了进一步的封装,简单代码如下1 H- D0 r3 O; ~5 }4 I& \- I' U
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
% T+ d. _% ~2 f3 X& O# `% O& |: |publicStrutsRequestWrapper(HttpServletRequest req){
5 E+ j, G( M2 [2 f! p+ ^super(req);: \ _1 I. L( L$ t) m
}( ^; E: v# |9 I0 Q
public Object getAttribut(String s){; u3 ]1 d6 r; A
ActionContext ctx = ActionContext.getContext();7 x* @2 G! s& v. l; h) M; ^
Object attribute = super.getAttribute(s)//先从request范围获取属性值
# t' l2 ?1 S. R# lif(ctx !=null){. Q- n" p2 S) ^8 L3 E
if(attribute==null){+ e1 e& d+ u+ I9 s
....
, V" X8 y. t1 d! c; V: C& A+ dValueStack stack =ctx.getValueStack();
5 L$ `7 y% s3 r- Eattribute=stack.findValue(s);//寻找规则就是前面的寻找规则 l; c& t- d. a
....1 d6 U' B5 h- w" G
}" Y2 W/ I1 T6 c' }
}
4 L+ }5 f! ?+ L7 r+ T( t6 }0 A# greturn attribute1 ^+ l) N( U2 W9 r! C# a1 Y
}8 x% ^2 c7 `1 F. Z. k' d
}1 J' V2 u/ F; I- @
EL表达式只能访问ValueStack对象中的内容- o( L+ {/ s) ^% R/ ]2 G
" u7 q0 w+ t2 \
(8).采用OGNL表达式创建List/Map集合对象,如果需要一个集合元素的时候(例如List对象或者Map对象),可以使用OGNL中同集合相关的表达式,使用如下代码直接生成一个List对象:
+ O* z4 L) F: h. G7 d1 ?<s:set name="list"value="{'zhangming','xiaoi','liming'}"/>把当前迭代的对象放在值栈的栈顶: }$ ~& {! w5 I2 _- n6 ?
<s:iterator value="#list"> X$ `0 C! ^+ d% X1 `
<s:property/><br>; K3 k+ N' U, I( }. F; |3 Q
</s:iterator>
+ m3 b4 v5 q6 r5 P+ v. ZSet标签用于将某个值放入指定范围
7 y' r7 Q/ b6 d! q# oscope:指定变量被放置的范围,该属性可以接受application,session,request,page或action.如果没有设置该属性,则默认放置在OGNL Context中.' ^' N1 j3 p- d
value:赋给变量的值,如果没有设置该属性,则将ValueStack栈顶的值赋给变量 ]2 \3 r- j$ c: H# ^/ G. H) C0 J
生成一个Map对象: U- m% |9 L; s: X" n6 y# G
<s:set name="foobar"value="#{'foo1':'bar1','foo2':'bar2'}"/>
7 o* ^5 y& J9 z, Z5 S<s:iteratorvalue="#foobar">//迭代标签,把当前迭代的对象放在值栈的栈顶(entry对象),foobar是Map对象和request等对象是同等地位,访问时需要使用'#'
1 \' q b ?% m$ q. M2 |<s:property value="key"/>=<s:propertyvalue="value"/><br>
: S4 @1 _% K# S! g( H</s:iterator>
; o. @) e. Y1 M1 ?. h数字不用任何符号,字符串使用单引号('),对于Map采用的是maps.entrySet()这个方式进行迭代的.
7 H# k, B4 _9 d5 z/ k$ K6 R! F1 w
% [& e3 T$ X, Y' p. G(9).property标签用于输出指定值:& l4 g2 a0 y7 s- s7 q6 D/ a
<s:set name="name"value="kk"/>
; L+ I- f( ^& @( R- f" J<s:property value="#name"/> }2 [8 g3 k8 ^5 j0 j. ^
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值7 b# C1 j! Q4 c, v( a
escape:可选属性,指定是否格式化HTML代码
+ V4 Y2 C% @$ I* Jvalue:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值7 j3 |2 o4 Z2 a. |
id:可选属性,指定该元素的标识; q/ g" g7 e3 }
8 t! N4 E. S; t L& J* G) K& ^
(1)0.对于集合类型,OGNL表达式可以使用in和not in两个元素符号,其中in表达式用来判断某个元素是否在指定的集合对象中,not in判断某个元素是否不在指定集合对象中,如下所示:
6 f+ E4 y( l1 s/ e) fin表达式:
$ T; d2 j$ A. ?) B% M8 y0 H<s:if test="'foo' in {'foo','bar'}"># N& R$ y$ P2 @2 b+ P( v9 V! _: m
在
% D! K$ d$ P: E" O</s:if>- ~2 @. ~. a7 f8 E/ E: @
<s:else>; k! [5 h- J5 ]+ \1 R& q# I
不在. N: y! e q& K. z" }
</s:else>) Q$ o* G. g' c% V% T( a
no in 表达式' f$ Q# c5 ^) n, R& O* H
<s:if test="'foo' not in {'foo','bar'}">+ h& H8 w* p. ], K
不在
' w3 `( I3 c) S& r* N0 M</s:if>
3 q% {% t* c+ j5 T1 k0 b<s:else>
; c" X/ ?( ?" k% X2 ]% g在$ Q* f. @. x6 ^" l+ l/ {, H
</s:else>, I( H* [+ W& @5 f, f
2 i& |9 f* l8 H& ^; P! m(11).OGNL表达式的投影功能,除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符:
" {: `6 ~/ X& O3 }: H* c第一:?:获得所有符号逻辑的元素$ K. k- _2 g$ T2 i; ?
第二:^:获得符合逻辑的第一个元素
) h; V7 Q, G- i. t第三:$:获得符合逻辑的最后一个元素' p& w; e3 C& K a0 M
例如代码:( D l6 V8 ]4 W0 u% N
<s:iterator value="books.{?#this.price>35}">/ I& R/ w" B5 g# Z4 D: W9 ^
<s:propertyvalue="title"/>-$<s:propertyvalue="price"/><br>& w# F! x( X9 |) R1 D# n# n+ U
</s:iterator>
! W( \, x) Y M! D' O在上面代码中,直接在集合后紧跟{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素,本例的表达式用于获取集合中价格大于35的书集合.
$ e/ G, f+ m2 B: Bpublic class BookAction extends ActionSupport{
0 f* \8 G- H7 v+ fprivate List<Book> books;
0 M2 y" S% |7 c# t& l7 E$ p: C5 z2 l' ` 8 ? u4 l6 V: w" h1 R' h
@Override7 M# t2 }) J* R0 [5 w2 k: x. @; n
public String execute(){9 r8 D$ L$ G$ }% V' I. q2 F
books = new LinkedList<Book>();' j9 A4 A7 Z6 u
books.add(new Book("aadfsd","spring",23));" q9 n, j; n+ z
books.add(newBook("basdfd","ejb3.0",15));6 l2 L) S# g4 j' f
}* m! K2 k/ `* e( D, ?
}) B: s* t8 n" |! A4 [9 a: M
" T- p( `, k$ E# N& U$ ~
4.result配置的各种视图转发类型; z2 f4 m" Q# P! w
(1).在struts1中有两种视图转发类型:容器内转发,容器外转发(重定向);
0 a4 g* [+ \# A; O+ Z; t<actionpath="/control/employee/manage".../>
" _; ~3 K' m. T6 [<forwardname="add">/index.jsp</forward>
. ~. k7 Z# i7 ^6 k5 n) h" a8 e<forward name="add"redirect=""/index.jsp</forward>
( k% H3 q8 W2 j</action>
: i# ]. I" U) I: H" K 2 b4 i) ?% `( ?. n# I
(2).struts2中的视图转发类型:result配置类似于struts1中的froward,但是struts2中提供了多种结果类型,常用的类型有:dispatcher(默认值)、rdierect、redirectAction、plainText;dispatcher对应于struts1中的容器内部请求转发,redirect对应于struts1中的容器外部转发(重定向)
% S5 M* Y' k6 P, e: k8 \ ' @' C* f3 G/ l) l/ F \: Y
(3).<actionname="helloworld"class="cn.itcast.action.HelloWorldAction">
) R4 i. o+ @! I9 ]0 @<resultname="success">/WEB-INF/page/hello.jsp</result>8 d" T" j0 r5 y, ], a% I! P+ p7 b
</action>
2 I, w& {9 T4 d% w在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应的action中的属性,如下:<resulttype="redirect">/View.jsp?id=${id}</result>,使用重定向可能需要将Action中的数据属性代入视图页面,这种方式太重要了,很实用.这种表达式叫做ognl表达式,struts1中是没有的,只能将属性值在代码中写死了,不像struts2中的这个表达式,很灵活.当传递的属性是中文时,需要进行URLEncoder.encode("传智播客","UTF-8")编码.+ [' A5 q- F) O$ C
6 [7 \! C. t' A& y- J0 i S
(4).下面是redirectAction结果类型的例子,如果重定向的action中同一个包下:
7 k" t% [; \; h7 R( U! m<resulttype="redircetAction">helloworld</result>
$ M a1 v4 v7 W* O$ w9 N如果重定向的action在别的命名空间下:
* y g3 q/ }9 [6 X<resulttype="redirectAction">8 i7 y+ ?1 u" q1 q" j
<paramname="actionName">helloworld</param>! W5 o7 u0 O! w7 ~# J8 r4 `
<paramname="namespace">/test</param>" {1 j7 L# H4 n- t: \: X$ H& q
</result>4 Z5 C* h% l4 E p$ n8 Z! l9 d
当添加用户完后,可以回到一个用户列表,此时可以重定向到action
: Y: _$ P u0 [/ E2 r
' U" V4 h7 O" E8 w7 J4 N( |! W, m(5).plaintext显示原始文件内容,例如:当我们需要原样显示JSP文件源代码的时候,我们可以使用此类型
: |9 Z+ L# p; b& r* X! ^<result name="source"type="plainText">
l8 G7 u: O% |' b3 e' H<paramname="location">/xxx.jsp</param>. N0 m! i% x, ?& f+ o7 ^
<paramname="charSet">UTF-8</param><!--指定读取文件的编码-->4 B9 g% X0 t/ u" v
</result>
# z) P. ] c r1 d$ n. [+ F在Eclipse中jsp是用UTF-8编码存放的,当读取jsp的内容时,是用本地字符编码的,可能出现乱码,所以要设置读取文件的编码集.+ C+ J3 u' V) }4 Q& `
, R* X c. Q ~. \# |(6).浏览器重定向的JSP不能放在web-inf目录中,而请求转发的JSP可以放在web-inf目录中/ @: d/ ~2 N" i+ {. B
) G. S' F9 c& `- @: L( ?. g(7).struts2中的全局视图:
! Z9 a0 m4 c2 Q8 f<global-results>- u) m1 h$ K9 w7 `6 P$ N2 P
<resultname="index.jsp"></result>" j$ j" Y2 d: G/ ]6 X3 \
<global-results>
" C" H, j# a: r& ]7 I和struts1中的全局视图是很相似的
7 R7 j+ J! D( j l6 c' F我们可以定义一个包,然后将全局视图的配置放到这个包中! J7 f: p0 C5 C
! H! W) ^. z4 ^5.struts2常用标签
9 x( ^9 |- v" |; J0 _! f( e(1).property标签用于输出指定值:
* K# Z5 d' ^8 [, m/ ]<s:setname="name" value="kk"/>% F" p* a( Z& E
<s:propertyvalue ="#name"/>8 C& a$ n& N3 L2 ?7 g
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值( ^" Y7 [, v# s% s; H
escape:可选属性,指定是否格式化HTML代码
x% f. I. I2 s, W4 ]' [value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值9 o' q4 |# u7 Q+ R) j
id:可选属性,指定该元素的标识% ^1 ~0 \4 S: R X7 n
8 D$ [, }% M/ l(2).iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组
4 b+ p% b. U& e( \<s:set name="list"value="{'aa','bb','cc'}"/>: b" X: q6 i& m+ v7 m) I& f# Z
<s:iterator value="#list"status="st">
# h: |8 ]9 N- B! O) q( V3 k<font color=<s:iftest="#st.odd">red</s:if><s:else>blue</s:else>>9 n" R! p8 @3 {/ R
<s:property/></font><br>; a+ v- ?5 J0 M1 n
</s:iterator>7 Y8 Y0 t. }, L& V+ a& e1 m
value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合,
/ m# y6 X1 G1 k G+ Cid:可选属性,指定集合里元素的id(已被标注为过时)9 _. f- Q1 E9 t) Q' k
status:可选属性,该属性指定迭代时的iteratorStatus实例,该实例包含如下几个方法:
, \: ?8 k. _1 p& H( V6 Uint getCount(),返回当前迭代了几个元素6 ~6 M5 S/ s; f! b' p( t: S/ I$ p% q
int getIndex(),返回当前迭代元素的索引
# ?$ w4 O. o. J- V& l+ Nboolean isEven(),返回当前被迭代元素的索引是否为偶数
( m1 E, m Q( ] Yboolean isOdd(),返回当前被迭代元素的索引是否是奇数. T( l6 t3 F5 S% O/ K- y f
boolean isFirst(),返回当前被迭代元素是否是第一个元素3 D0 y( E, I: Y
boolean isLast(),返回当前被迭代元素是否是最后一个元素
- X; V- }, `% Y- [9 J
: X: h4 M/ M2 }- ^2 e(3).<s:setname="age" value="21" scope="request"/>1 B( L3 {) ^3 N5 ~, V- K$ c
<s:iftest="#request.age==23">
$ A" _# h) B. c) F23- Q q" p& b4 j: F* U V$ J
</s:if>
: X8 ~9 A0 n0 |' r<s:elseif test ="#age==21">: E4 c" G5 D( j5 z$ A
21; L+ H% I4 h2 `" e- I) o6 U- U
</s:elseif>
5 L. q! E$ |- ? _* [* e<s:else>. R. r# Q6 ~" Q4 N9 D9 x
都不等
' e0 u* [) m+ N: F* z</s:else>
5 R& R5 L& x6 O4 F$ m. [ # E, \6 F; `1 G' X7 D$ y: Z5 A) D
(4).url标签:6 U N4 ~& z) b
<s:url action="helloworld_add"namespace="/test">
1 H8 e( g$ J# p# c: S<s:param name="personid"value="3"/>
$ z5 G5 G5 ]( \" K' P; n</s:url>3 W( \3 Z$ N: U
生成类似如下路径:
) a; c3 @/ G, l; q) y2 ?+ G/struts/test/helloworld_add.action?persionid=36 Y( B6 S: U7 j- a0 R
8 E I+ w% J: h8 P9 ~7 n
当标签的属性值作为字符串类型处理时,"%"符号的用途是计算OGNL表达式的值
' c l9 q) g( d% f0 p# f0 Z1 f<s:set name="myurl"value='"http://www.foshanshop.net"'/>1 n" J8 W- a( Z- ^* V" D
<s:url value="#myurl"/>( v% T) `0 G& S+ @4 s. \8 ?
<s:url value="%{#myurl}"/>
+ e% A, P' k6 a. ?! a7 E0 T输出结果:( L2 s& K) b/ F$ c( ]
#myurl2 J8 g! H; R. H. Q1 F- R+ q
http://www.foshanshop.net! }' E* U. s( t
; W* v. Z0 U+ Y. B/ O$ Q$ x(5).表单标签checkboxlist复选框
- R5 h+ H6 N, ~如果集合为list( ?' \' k" T$ V6 r$ g
<s:checkboxlist name="list"list={'Java','Net','RoR','PHP'}" value="{'Java','Net'}"/>
' A8 B8 o3 n. {5 h9 f+ h3 K<input type="checkbox"name="list" value="Java"checked="checked"/><label>Java</label>! u# W, b2 q& Q3 f. c+ c
<input type="checkbox"name="list" value="Net" checked="checked"/><label>Net</label>
$ B% s4 j. O6 ?" l' e<input type="checkbox"name="list" value="RoR" ><label>Java</label>
( J2 ^0 C& j1 q7 _/ \+ n0 J<input type="checkbox"name="list" value="Java"/><label>Java</label>
+ x- G/ N2 u, i% ^) @! Z# K/ o如果集合为MAP5 k; ]8 j: f Q' P1 h
<s:checkboxlist name="map"list="#{1:'aa',2:'bb'}" listKey="key"listValue="value" value="{1}"/>
* i! i6 B- `! ~0 X# g生成如下html代码:/ c9 K J, h! s9 H: |( ~; U0 u, S8 Y3 x
<input type="checkbox"name="map" value="1"checked="checked"/><label>aa</label>
r% }) @8 z/ u/ I* X' b! K7 c- n$ H<input type="checkbox"name="map" value="2" /><label>bb</label>- O! h0 I5 C( \* G" ?& U0 X" a
当然集合里面存放的也可以是对象类型8 ]1 t& r6 Y7 O( M' X- x! ]9 \3 `
2 I ?' ^) W1 ]' z3 B(6).单选框3 m7 v) l* e2 K( g9 x- G( Q
<s:radio name="beans"list="#request_person" listKey="personid"listValue="name"/>8 S* w7 a- Y9 i
& m# f& O8 W/ X6.struts2的处理流程与Action的管理方式; K3 w" T& n/ q# h. H; ^
(1).用户请求->(查看web.xml文件)StrutsPrepareAndExecuteFilter->Interceptor(struts2内置的一些拦截器或用户自定义拦截器)->Action(用户编写的Action类,类似Struts1中的action,针对每一次请求,都创建一个Action)->Result(类似struts1中的forward)->Jsp/html(响应)
, |! U5 G" L. m. O. |1 } 9 i4 U0 K2 b6 z$ ]0 v+ l
(2).StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它负责拦截由<url-pattern>/"</url-pattern>指定的所有用户请求,当用户请求到达时,该fileter会过滤用户的请求,默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入到struts2框架处理,否则struts2框架将略过该请求的处理,当请求转入struts2框架处理时会先经过一系列的拦截器,然后再到Action,与struts1不同,struts2对用户的每一次请求都会创建一个Action,所以struts2中的action是线程安全的.( P/ C2 V/ g: ~3 h ?5 R
6 a! z# t1 d: r7 G) q, F7.XML配置方式实现对action的所有方法进行校验
" O8 U; N1 @( Z0 b" R8 Z(1).基于XML配置方式实现对action的所有方法进行输入校验:
5 z/ N8 L! J7 d- o5 V使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中,ActionClassName为action的简单类名,-validation为固定写法,如果Action类为cn.itcast.UserAction,那么该文件的取名应为:UserAction-validation.xml,下面是校验文件的模板:& e5 w# j3 c5 I
<validators>. f6 i6 w' x' t) e& I Y+ P1 I
<field name="username">1 E: H4 F0 w9 R0 R) m
<field-validatortype="requiredstring">
& |4 j- O: J- {+ @" u3 B+ g$ r9 k4 u<paramname="trim">true</param>
8 W9 c6 g4 i; ?, |5 k, N<message>用户名不能为空</message>- H8 j' m, M; q; b
</field-validator>
4 |( b3 M) r4 a7 {- X, T5 x</field>/ k6 H }8 s, k1 O6 U
</validators>, ?- k, e O+ b9 A: b( O+ O; x
<field>指定action中要校验的属性,<field-validator>指定校验器,上面指定的校验器requirestring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到,<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中的key,在这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空.8 Q7 P" x# N* n+ F# d
2 u4 f. s/ E$ D2 B: R(2).struts2提供的校验器列表:- l% ~/ {/ F7 C
required(必填校验器,要求field的值不能为Null)
, ^3 S* a$ G. H! Zrequiredstring(必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串取钱后空格)* `& Z& M3 O& X' ~: u i6 _) B
stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)2 N' r p. N) b1 f5 K2 D
regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
# O8 G! L/ |0 h8 E5 Aint(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
0 @- d# Q f% g7 P# ~$ K- mdouble(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
5 b3 I6 |4 U$ lfieldexpression(字段OGNL表达式校验器,要求field满足一个OGNL表达式,expression参数指定OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
1 d+ ]8 A3 i* v" N- J! u0 Jemail(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)7 Y# ] r' K2 f6 }2 i9 e
URL(网址校验器,要求如果field的值非空,则必须是合法的URL地址)
3 n8 W, f1 D# z! I+ p jdate(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
6 Q4 [6 \1 |7 ^4 i0 k; d8 econversion(转换校验器,指定在类型转换失败时,提示的错误信息)
* i! u6 q# S7 r4 ^1 Vvisitor(用于校验action中的符合属性,它指定一个校验文件用于校验符合属性中的属性)3 e' n& `7 q( U) ~
expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中); p* k! _# g1 _" `* n$ ]- w" E
" b. v$ j& A& I
(3).![CDATA[文本内容]]:文本内容不会被解析,只会原封不动的当做文本处理
1 |' |. ]% `* y - U! b. M- T* z1 a
(4).编写校验文件时,不能出现帮助信息:2 Q% \6 |% ^& e: L# u" L
在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按照下面方式解决:( _$ d% e6 {. U5 S
windows->preferences->myeclipse->filesand editors->xml->xmlcatalog:点击add,在出现的窗口中的location中选"file system"然后再xwork-2.1.2戒烟目录的src\java目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候,不要急着关闭窗口,应把窗口中的Key Type改为URI,Key改为http://www.opensymphoney.com/xwork/xwork-validaor-1.0.3.dtd
- n5 r8 Z$ h1 z" [. Z" B" q% R
; z4 b* Q" ]/ b. z/ g8.XML配置方式实现对action的指定方法校验
4 O" x; {$ z1 X/ C9 p' j' \: o(1).基于XML配置方式对指定action方法实现输入校验:7 ?8 \7 r6 W" d8 k( c
当校验文件的取名为ActionClassName-validation.xml时,会对action中的所有处理方法实施输入校验,如果你只需要对action中的某个action方法实施校验,那么校验文件的取名应为:ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中的action的名称,例如:在实际应用中,常有以下配置:
8 ^% G* c* n* O# P5 V. D<action name="user_*"class="cn.itcast.action.UserAction" method="{1}">
8 _" s1 C9 m5 b D( W<resultname="success">/WEB-INF/page/message.jsp</result>
% h2 l/ m- c6 Z: ?6 K) [<resultname="input">/WEB-INF/page/addUser.jsp</result>9 `( h2 [# g; d4 c& B8 ]4 t& u
</action>3 s0 T6 T, t6 L. x( ~& X M2 N
UserAction中有以下两个处理方法:
/ Q# Y5 p( K2 d2 m, L2 t |* s$ opublic String add() throws Exception{
, q1 z, U! E% c% d v H; j + H# w$ }0 j- N: {7 V7 H s( M7 E
}. I6 m4 p! x1 [5 m i# g( @ x
public String update() throws Exception{/ D" v1 S( }" @' ], W) ]5 W; m9 S
) v0 P- q7 v# M q# [} V7 o. u W t. ^$ ~9 K- U% S
要对add()方法实施验证,校验文件的取名为:UserAction-user_add-validation.xml
9 Z n8 s4 i N. t' o8 n W要对update()方法实施验证,校验文件的取名为:UserAction-user_update-validation.xml
& h, e6 ?. y _ h# t0 ^! }9 G+ x
# W: {+ `+ B p1 X(2).基于XML校验的一些特点:
' u3 h m6 _' ^* }/ t7 X" B! F+ r当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
3 O* ^) Z3 v+ jActionClassName-validation.xml3 l7 K3 H$ e) z7 ]: Q: J+ n
ActionClassName-ActionName-validation.xml
7 w' d: T, Q% t系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验,如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的规则。0 G7 I. l2 O5 [# i3 Y- A9 g
当action继承了另一个action,父类action的校验文件会先被搜索到% |# O9 }3 I5 ^- O6 `' W
假设UserAction继承BaseAction
3 ]3 v N/ l; m% }/ A<actionname="user" class="cn.itcast.action.UserAction"method="{1}">: S: A. M$ {* F; _% m P$ p
</action>8 B# Z4 Y# G3 A. t
访问上面的action,系统先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user-validation.xml,应用于上面action的校验规则为这四个文件的总和
5 ]" i1 J" H* z* D$ L( x 3 g& U7 X! W! M3 l2 ^. Y: D
9.动态方法调用和使用通配符定义action) A. ~2 M, i3 X! V& r+ [
(1).在struts1中实现方法的动态调用:& ?5 T4 U! |5 g! h! t4 J1 \
<actionpath="/control/employee/manage" type="....DispatchAction"parameter="method"/>" G% g; r: u& R3 y: g* I
</action>
% H% g8 O, F- O/ j6 ~9 h2 s/control/employee/manage?method=addUI0 t" x! T- H* l* n/ h
但是Action必须继承DispatchAction4 K7 x' O4 h( O( q- O# y
5 L/ q9 s, ~. U% S(2).在struts2中有两种方式:; F! l' u9 W/ V
第一种:(struts2.1版本后就不建议使用了)是动态方法调用:如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法,如下:7 b* u' P4 h L- V: ^1 D& ]% ~ R8 R% Y
public class HelloWorldAction{3 M" k& C5 K {) [: O1 f/ O
private String message;
) W9 G* }$ k$ y....
. Q" W- Z0 ^2 L F! Rpublic String execute()throws Exception{
" G9 ~$ T1 B: \; r1 N9 |' g this.message="我的第一个struts2应用";
: R# B0 v1 o9 Y* k' u}, F- S# M- j+ ]( K A& ~4 |" x7 c: D, c
public String other() throws Exception{8 k* e' J3 \2 `7 y1 p+ t
this.message="第二个方法";$ H; r j. t/ M2 r5 p$ I X
return "success";
, q. j+ B2 L8 E) W) e* i1 j}
4 B0 w4 \6 _, b7 b- w/ s% n0 b}
7 |8 v; v/ ~! o: @& n* `+ O& T假设访问上面的action的URL路径为:/struts/test/helloworld.action,要访问action的other方法,我们就可以这样调用:/struts/test/helloworld!other.action,如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用:
8 ]: Z1 b# a2 t7 K<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
( k% y- ^4 d+ z. U; {( q第二种:使用通配符定义action(推荐使用的)
4 G9 N8 U( A0 Z; J% m% ?4 t<package name="itcast"namespace="/test" extends="struts-default">- B: \( C: D) W9 I7 g
<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}>$ f4 c1 E1 }$ L8 Z" t6 X5 M% \/ F# }
<resultname="success">/WEB-INF/page/hello.jsp</result>; |( O, _7 z2 H
</action> Z; M$ [7 O. h P+ f
</package>
$ v1 I$ ~8 D# b# E% y( L) m" q+ t- Fpublic class HelloWorldAction{; i _0 F; K0 p8 M* S. _
private String message;" x9 a# ` ^; ]
....
/ h5 x# i9 O* @' p, `1 apublic String execute()throws Exception{
5 v* y! [4 U( O' K this.message="我的第一个struts2应用";
" b# l6 _2 b9 I% G2 A( }}
" Z$ f( X/ B7 s* o* H" F( @' p, vpublic String other() throws Exception{
5 a$ O1 x* C- z, y( g8 d& c! Bthis.message="第二个方法";9 h# T, }+ a# w1 w
return "success";
, }( I: `; s, C* ~1 X}3 j# A" @+ R! ]2 X
}
* x/ Q8 q7 u+ s M$ o" _$ Y9 L要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action) {, ~* p7 k/ |1 Q. _ T7 S- e* ^6 J
name="helloworld_*"后可根据多个*,method={1},'1'表示匹配*的位置5 Y) \2 K% E5 I2 V1 S) o6 W
name="helloworld_*_*",method={2}:要访问other()方法,可以通过这样的URL访问:/test/helloworld_xxx_other.action
) V7 r1 \+ k- c4 z
, q% F# P ~1 R- | x7 f3 N6 F) l10.对action指定的方法进行校验
/ i2 I1 n. W) y4 {4 s) k手工编写代码实现对action指定方法输入校验:' d3 Q+ K0 W5 {
通过validateXxx()方法实现,validateXxx()只会校验action中方法名为Xxx的方法,其中Xxx的第一个字母要大写,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息,(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息.
$ j' U8 p1 F1 @0 E$ UvalidateXxx()方法使用例子:
! j+ J3 K) Q7 A: L% l hpublic Stringadd() throws Exception{return "success";}% \. K# G4 e% `8 y* ?* [
public voidvalidateAdd() {
5 ` {7 _$ r* t6 \4 k- Yif(username==null&&"".equals(username.trim()))this.addFieldError("username","用户名不能为空");# a- N* g/ Q) ~. t
}
4 }- @# r* x1 B) ?5 q# D0 ~! v验证失败后,请求转发至input视图:<resultname="input">/WEB-INF/page/addUser.jsp</result>* b2 w x+ o& _$ M t) s+ ~
在addUser.jsp页面中使用<s:fielderror/>显示失败信息5 K+ t: P; ?9 i9 _4 S1 j
! X5 L9 e$ R' q8 d/ P1 F11.对Action中所有方法进行输入校验
0 q. Y5 V* Q) C! |! a+ ^! \5 ?(1).在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验
' _" W) C ^. I0 n2 j0 A w2 ~3 {& I' K2 [# `0 g8 t2 j. H8 y
(2).对于输入校验struts2提供了两种实现方法:一种是采用手工编写代码实现,另一种是基于XML配置方式实现
% D, k) u- G0 g4 S$ w 7 l. Z+ J. H. I/ c) y
(3).手工编写代码实现对action中所有方法输入校验:通过重写validate()方法实现,validate()方法会校验action中所有与execute方法签名相同的方法,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息.
0 ~- P2 ?: `$ n. Tvalidate()使用例子:
' ~% p0 F. g% m7 H9 ]public voidvalidate(){
% Z F# v S8 W @if(this.mobile==null||"".equals(this.mobile.trim())){this.addFieldError("username","手机号不能为空")}else{if(Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matchers()){this.addFieldError("mobile","手机号的格式不正确");}}
# [2 ?/ B, T0 f& x}* m1 C4 ~& r) {- E6 ~1 a, [% k% I
验证失败后,请求转发至input视图:
! ^+ M- c; W' L9 P- z3 P0 H. Y. ^7 Q9 J<resultname="input">/WEB-INF/page/addUser.jsp</result>$ u& z0 G0 K2 @& w9 I; m; p! z9 \
在addUser.jsp页面中使用<s:fielderror/>显示失败信息, Y) _; Q% W. q+ l
: [# ~2 p3 v% x5 n( T |
12.多文件上传: w5 L$ R. _& Z
(1).多文件上传,就是在一个文件上传的基础上,将属性File变成数组类型File[]类型即可,同时该字段的名称必须要和上传页面中属性name的名称一样.4 J. L3 S! F' x' |! o1 _
然后进行一次迭代,就可以得到所有的文件
& D0 e9 ? [/ S# l( l4 {8 Y T7 p9 F9 ]: w. J4 _% w
13.防止表单重复提交% W J( y+ `& L8 f1 ?3 x$ C
<s:token>标签防止表单重复提交7 Z2 {' P+ j* t ^9 H
第一步:在表单中加入<s:token/>( c. B- u* i* k M) ?, i Z2 W
<s:form action="helloworld_other"method="post" namespace="/test"> {0 E: h* a! z
<s:textfieldname="person.name"/><s:token/><s:submit/>2 u) d2 @6 H% l- l
</s:form>
0 R! Y8 m3 }) X; i- R- g第二步:<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}">
# k7 [7 S& {; Z5 J<interceptor-refname="defaultStack"/>: B, |8 t2 \0 ^0 r4 a" _
<interceptor-refname="token"/>
7 ~3 w% R$ H% M# A7 M1 u<resultname="invalid.token">/WEB-INF/page/message.jsp</result>
5 d7 a1 g j0 J' g% ?" i<result>/WEB-INF/page/result.jsp</result>* }8 g3 G4 t J, N! j3 Z# c3 a
</action>8 G( Q/ h$ n) _' H) D& A
以上配置加入了"token"拦截器和"invalid.token"结果,因为"token"拦截器在会话的tlent与请求的token不一致时,将会直接返回"invalid.token"结果8 N% m( i& x2 _( T7 u
在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误
4 O& {2 h) ~1 S' k1 S使用了<s:form/>标签可以不指定action的上下文标签路径,可以通过命名空间实现.和前面的原理是一样的,在路径后面添加上sessionid号,只是这步操作不需要我们自己得到sessionid号,struts帮我们操作.
g2 \6 o3 A. e/ i在值栈中的对象,访问无需添加'#') f5 v+ M( ^6 m# A, f* @0 S9 i
; A6 M) o( @! u1 B$ e
14.访问或添加几个属性3 s" Z1 e$ E7 M1 _9 {) |; O
(1).访问或添加request/session/application属性,在struts2中的Action中的execute方法中没有Servlet api(没有响应的参数);
2 s/ _& S; U; x: M& Z# E4 opublic String scope() throws Exception{
9 `! J7 ~4 \* m! Y1 P: pActionContextctx=ActionContext.getContext();
) Y$ s/ R7 m# u3 a/ N/ J% Tctx.getApplication().put("app","应用范围");
5 w: Y' u) L0 x( H, Zctx.getSession().put("ses","session范围"); A+ Y( D8 ^; L- _! E5 x
ctx.put("request","request范围");2 h% p: |/ P! p' J3 R# y5 E E6 `
return "scope";9 ~1 X$ c$ x+ }7 Q) V
}# E1 P$ Y1 a- @! B1 k
0 q4 o+ M9 A! J
<body># Y6 S- l V% X) K' N
${applicationScope.app}
! l; |+ A% J3 M0 W( e${sessionScope.ses}
, X8 ?5 b* G0 ^# a9 n0 z${requestScope.req}) H/ L2 k2 @, H. i2 F1 S
</body>+ U2 i# b& Z9 q0 k
6 t: c/ c) x" ~+ m! S0 ^0 V
(2).获取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse对象:
% h4 M" ?4 y+ C1 ]方法一:通过ServletActionContext类直接获取
2 R1 v6 H7 ^% e N( p/ t1 cpublic String rsa() throws Exception{
& [" R& {! }# C3 b) I% KHttpServletRequest request =ServletActionContext.getRequest();
& P) U) H$ g5 bServletContextservletContext=ServletContext.getServletContext();0 h6 y9 f& N0 G# A. v( @
request.getSession();
# h- R/ i4 h a4 P0 _# R. PHttpServletResponseresponse=ServletActionContext.getResponse();
* g$ B7 ~$ E I9 `7 c) c- h6 Qreturn "scope";: `1 V; N- o2 I7 j, g
}. D( }$ m1 P7 `4 r& Z( v/ G5 r
方法二:实现指定接口,由struts框架运行时注入:
4 l# n: `, a$ }/ R `9 F" c- A7 Kpublic class HelloWorldAction implementsServletRequestAware,ServletResponseAware,ServletContextAware{ _7 J$ D. y. d3 h; V# F2 m! @3 e
private HttpServletRequest request;
/ V9 y: `' f6 o3 }/ d# h S! E2 ?5 Sprivate ServletContext servletContext;
4 A# ^/ e3 S, R& e$ b6 C j7 @private HttpServletResponse response;
! q' x5 t7 p! R' Wpublic voidsetServletRequest(HttpServletRequest req){4 V, ^4 |8 B% }- K6 L; P$ b3 @ s' o
this.request.req;6 K E' D* J% F1 e# {: R R
}, Q. e, h; g% e: x6 U* _
public voidsetServletResponse(HttpServletResponse res){: m# a8 g4 ]; T! g- h, J
this.response=res;3 a8 F9 W. ?$ H) L5 I: t
}
4 F5 q6 z- D0 F2 s: npublic voidsetServletContext(ServletContext ser){
1 d6 A0 h# {1 zthis.servletContext=ser;! ?9 p1 a$ E8 d/ D) x$ F$ y
}
/ |4 z) O& X. p/ a9 E, e2 S注意1和2的不同,一个不需要得到对象,一个需要得到对象,所以要区分两个的应用场景
, x. k% ^/ K3 ]# Y. o}
g2 J! J( s( G6 u' s/ [8 l 2 j7 ~" T" `0 c8 _$ C: j/ `
15.解决struts配置文件无提示问题/ v: ?6 l$ L% l3 X& z& h+ M$ ^
找到struts2.0.dtd文件即可,windows->preferences->MyEclipse->XML->XMLCatalog,点击添加strut2.dtd& g3 H% R7 X) k
! e) D+ w5 ~ {0 Y
16.介绍struts2及struts2开发环境的搭建: Z8 x, W' g) j8 u/ @
(1).struts2是在webwork2基础发展而来的,和struts一样,struts2也属于MVC框架,不过有一点大家需要注意的是:尽管struts2和struts1在名字上的差别不是很大,但是struts2和struts1在代码编写分割上几乎是不一样的,那么既然有了struts1,为何还要推出struts2,主要是因为有一下有点:
8 v$ m1 D e8 P Y第一:在软件设计上struts2没有像struts1那样跟Servlet api和struts api有着紧密的耦合,struts2的应用可以不依赖于servlet api 和struts api,struts2的这种设计属于无侵入式的设计,而struts1却属于侵入式设计,因为其的
; D+ T3 J) K6 qexecute()方法中的参数为ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse/ a9 ^+ O6 R! D2 `. S: i& I$ p
第二:struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能( ]( o; _$ Z, I
第三:struts2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型,在struts1中,如果我们要实现同样的功能,就必须向struts1的底层实现BeanUtils注册类型转换器才行! V) T# J: q# l6 a4 ?+ @8 y
第四:struts2提供支持多种表现层技术,如:JSP,freeMarker,Velocity等
, m5 e- @3 j8 G第五:struts2的输入校验可以对指定方法进行校验,解决了struts1长久之痛,struts1中的validate方法对所有的方法进行校验
+ z- e7 j: A- g6 v4 o: m第六:提供了全局范围、包范围、和Action范围的国际化资源文件管理实现.% b) L" O$ o9 j; \, ?: v- C
! `& P! v& c( a1 w2 k
(2).搭建struts2的环境和struts1是相同的,第一步导入相关包,第二步建立struts2的配置文件,第三步在web.xml中注册struts2框架的配置$ m' Q0 F* v& R
0 {% g) S3 d0 a- k
(3).所需的包:struts2-core-2.x.x.jar,xwork-2.x.x.jar(webwork的核心架包),ognl-2.6.x.jar
! V0 b3 M6 J* [4 x4 P: o
- n5 h+ u. V2 @: g! P8 E(4).struts2默认的配置文件为struts.xml,该文件需要放在/web-inf/classes目录下, z) C {& b6 y( p( |1 a
0 ?/ t( P! f! H: S0 k
(5).在struts1中,struts框架是通过servlet启动的,在struts2中,struts框架是通过Filter启动的,它在web.xml中的配置如下所示:可以参照struts文件夹下的例子中拷贝,在strutsperpareExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作,注意:struts2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件* @# W9 R2 Y" W. ]+ m9 x, O
+ G$ h, {- h l3 l% D(6).自从struts2.1.3以后,下面的FilterDispatcher已经标注为过时了,struts2.1.3后期版本为StrutsPrepareAndExecuteFilter类: W* e& |, z9 Q% I! d0 K
- _. Z9 M: h+ z$ N
17.开发第一个应用
}) A0 \' x* i(1).在struts.xml中的配置:
5 L' N0 n( H0 o. \# o/ a4 l<package name="itcast"namespace="/test" extends="struts-default">
. T$ |. d6 s8 j& }1 Q' F, i' t# _$ l <actionname="helloworld" class="cn.itcast.action.HelloWorldAction"method="execute">3 g+ m2 |9 O( t5 Y# S5 @. [
<resultname="success">/WEB-INF/page/hello.jsp</result>! G' G1 c# m8 V( B5 D$ r. F8 z
</action>
, y. N" U2 N. V" m7 \* P w</package>& q. {+ s3 Y7 n3 {1 G$ k {+ l
在struts2框架中使用包来管理Action,包的作用和Java中的类包是非常类似的,它主要用于管理一组业务功能相关的action,在实际应用中,我们应该吧一组业务功能相关的Action放在同一个包下
$ p( E3 U2 b n* z配置包时必须指定name属性,该name属性可以任意取名,但必须唯一,它不对应java的类包,如果其他包要继承该包,必须通过该属性进行引用,包的namespace属性用于定义该报的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:/test/helloworld.action,namespace属性可以不配置,对本例而言,如果不指定该属性,默认的命名空间为" "(空字符串).当然配置可以减少重复的代码,struts1中的重复代码就可以使用命名空间来解决
8 _+ _5 O3 f5 r0 E* ]通常每个包都应该继承struts-default包,因为struts2很多核心的功能都是拦截器来实现的,如:从请求中把请求参数封装转到action、文件上传和数据验证等都是通过拦截器实现的,struts-defaul定义了这些拦截器和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,可以查看struts-default.xml文件中,就可以看到定义了很多拦截器; O. l0 N% h( y' y* b
<result></result>和struts1中的forward很相似,定义视图7 a0 E5 k; d9 D/ U% o8 X B0 T
( m; z# K E+ f0 I: {" n# E' t(2).public Stringexecute(){return 视图的名称;}注意到这个方法和struts1不同,没有参数,返回类型也不同,这就降低了耦合性,非侵入式的编程了.
3 c4 g* N; }* i* T9 ^
# z0 n3 K/ r: ^; W, \+ k- Y(3).在jsp中使用el表达式即可${message},message是Action中的一个方法getMessage()方法,而不是根据Action中的成员变量message
" v L r1 n2 N6 F" g( n! H* a1 h / T7 c3 v) p1 c5 ^
18.配置Action范围国际化资源文件
9 |& w' C! ^: ^! F5 H3 ](1).我们也可以为某个action单独制定资源文件,方法如下:在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为Action类的简单名称当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查找基本名为package的资源文件,一直找到最顶层包,乳沟还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中查找
* h0 ]1 g% T& v6 \ ( R- }0 N* U$ I/ V# a
(2).JSP中直接访问某个资源文件" a- t' O& y. m, N# N
struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:
1 g6 I& }& q1 b+ A<s:i18n name="itcast">
% b; ]/ G* s6 O<s:text name="welcome"/>
: H1 O. W# s/ l0 p$ ?" D</s:i18n>
' a' c# d" H9 D3 P8 o' s) sitcast为类路径下资源文件的基本名
! w1 s. n. o9 R' D3 q Y* Z: I% a如果要访问的资源文件在类路径的某个包下,可以这样访问:' l' ]) |" y( F+ P, l+ C
<s:i18nname="cn/itcast/action/package">/ y! k+ O4 ~, {: T
<s:text name="welcome">: b4 Z& S; v, g9 ?/ Z( @
<s:param>小张</s:param>( {! I' I# `2 a! W' q
</s:text>8 c. F5 _) j: j2 o4 b% V
</s:i18n>$ A2 o5 q# k- m9 t3 R. @
上面访问cn.itcast.action包下基本名为package的资源文件
; P2 B( H) v% _' O' Z1 h7 G
- S4 T, {+ T f19.配置包范围的国际化资源文件
) l( J7 H7 n' r/ O" W/ n(1).在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都放置在全局资源属性文件中,显然会导致资源文件变得过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件
3 b5 Y) `0 m$ q* V, Q! P方法如下:在java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源,当查找指定key的消息时,系统会先从package资源文件中查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找.% i; y6 e5 F* r
% P. L9 ^ t. m. L* l5 C20.配置国际化全局资源文件、输出国际化信息# \8 ~/ T9 K- o, A9 ~
(1).准备资源文件,资源文件的命名格式如下:
7 [: `9 f" i$ c @6 t9 xbaseName_language_country.properties$ z: T# y9 u/ o) x
baseName_language.properties
3 s- R3 ^# B! \/ V2 z# dbaseName.properties
! b4 N7 x/ L0 c" ?5 G3 G3 {' v其中baseName是资源文件的基本名,我们可以自定义,但是language和country必须是java支持的语言和国家。如:
! _- D& s6 Z; b+ `' S中国大陆:baseName_zh_CN.properties- w) n' }% S0 I& u" N1 X
美国:baseName_en_US.properties
' v+ X' d8 I0 K# W% t7 t
N B# I" D1 i% u( _+ G(2).现在为应用添加两个资源文件:
7 T$ z. e4 { L9 p' b6 W第一个存放中文:itcast_zh_CN.properties
, T) a2 ]3 q; c4 S内容为:welcom=欢迎来到传智播客
& y1 X; @0 V+ M# I+ `5 I第二个存放英语(美国):itcast_en_US.properties( \" ?7 w6 v) C. S& A" T
内容为:welcome=welcom to itcast8 v8 n5 p, M6 j1 J1 q6 m
* q' e6 t( r% k$ x(3).对于中文的属性文件,我们编写好后,应该使用JDK提供的native2ascii命令把文件转换为unicode编码的文件,命令的使用方式如下:
/ X) r4 Q4 Z* h0 Gnative2ascii 源文件.properties 目标文件.properties,在MyEclipse6.6版本以及后面的版本会自动转换.
/ _5 s1 s) x c! I7 f, O+ \4 a( e8 e : X3 n; A7 \4 b' L- _/ T* j. L
(4).struts2有:全局范围,包范围,action范围的资源文件
9 Y7 o" }; N5 w2 W# k2 n
% r6 V/ A x# S(5).配置全局资源与输出国际化信息:
$ w, l, w4 t5 s当准备号资源文件之后,我们可以在struts.xml中通过:struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:! B: S- h9 C* W1 l2 T+ X0 X8 v n. g
<constantname="struts.custom.i18n.resources" vlaue="itcast"/>
z% b9 Z$ w* h8 D2 ?6 Jitcast为资源文件的基本名
. z$ Z/ j) Z6 o' h. M4 ] `后面我们就可以在页面或在action中访问国际化信息:
( y5 F, f4 Y0 _% Q在JSP页面中使用<s:text name=""/>标签输出国际化信息:
9 e4 ~; |! A0 W( x# _) E<s:textname="user"/>,name为资源文件中的key
& I8 l/ M6 r" Z在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该该方法的第一个参数用于指定资源文件中的key,! b% J1 E& a _6 x0 I
在表单标签中,通过key属性指定资源文件中的key,如:$ V) E6 r; L) M
<s:textfieldname="realname" key="user"/>
6 c3 x1 w) Z& h8 h" _. a$ [8 _
7 P9 r# I& C* M" y" h! r21.请求参数接受 M x9 w [( W) q& X1 D e& U
(1).struts1中是使用ActionForm接受用户的请求参数
9 H+ V# z1 i) I0 m- I
* z6 I5 o; E7 O! ^(2).采用基本类型接受请求参数(get/post):- m! T- ]2 |9 w9 O; ^/ ?$ U1 t
在Action类中定义与请求参数同名的属性,struts2便能自动接受请求参数并赋予给同名属性:请求路径:http://localhost:8080/test/view.action?id=78 A C7 i; Z2 @- X- K
public classProductAction{& G3 w$ F% c l9 P
private Integerid;* h+ g2 O5 N' i/ N6 l" i0 k
public voidsetId(Integer id){//struts2通过反射技术调用与请求参数同名的属性的setter方法获取请求参数值
% e2 |. N7 l9 Y2 vthis.id=id;- l0 v% ?; q5 l7 D* m. L' c* m7 n
}
5 {: Q/ s' T' y) i7 |+ @" zpublic IntegergetId(){return id;}
' l5 |; h, Z- ~4 s3 d4 F}, C3 k1 N, P2 }- B0 p9 e" Q4 J4 h
1 }0 [: B; j/ X4 ]
(3).采用复合类型接受请求参数+ X, ]/ M8 T. Y9 I1 G- A4 f
请求路径:http://localhost:8080/test/view.action?product_id=78& @$ n2 t) i3 e5 u
public class ProductAction{% l' Q: u' }6 {6 u5 p' ]
private Product product;3 @' x+ d, _/ b9 V
public void setProduct(Product product){htis.product=product;}
, `/ S8 ~! o" k9 ]public Product getProduct(){returnproduct;}3 \& q$ E5 c `- {7 M5 @
}* k* N* [! N: D
struts2首先通过反射技术调用Product的默认构造器创建product对象,然后再通过反射技术调用product中与请求参数同名的属性的setter方法来获取请求参数值0 g! q1 \: X: f+ u, D" E1 K- N n
9 |, }3 q: |4 l! z: o(4).关于struts2.1.6版本中存在一个Bug,及接受到的中文请求参数为乱码(以post方式提交),原因是struts2.1.6在获取并使用了请求参数后才调用HttpServletRequest的setCharacterEncoding()方法进行编码设置,导致应用使用的就是乱码请求参数,这个Bug在struts2.1.8中已经解决,如果你使用的是struts2.1.6,要解决这个问题,你可以这样做:新建一个Filter,把这个Filter放置在Struts2的Filter之前,然后再doFilter()方法中添加以下代码:+ d; S7 Y: G ~. ]. X/ b
public void doFilter(..){0 H( O* J& z6 b0 \! G& {' m5 _
HttpServletRequest req=(HttpServletRequest)request;4 W; d# |/ D! r' I- c. s" W
req.setCharacterEncoding("UTF-8");( I9 _% P: t/ X- q+ F7 s# `
filterchain.doFilter(request,response);- W$ e' A9 y3 z: f% P
}
; s' t. B8 l! y% s3 I" y6 B
; O, `1 O8 _+ q. y3 ~22.全局类型转换器
$ w* m1 W- H& E& B* W, l# q& H- r自定义全局类型转换器:将上面的类型转换器注册为全局类型转换器:在WEB-INF/classes下放置xword-conversion.properties文件,在properties文件中的内容为:待转换的类型=类型转换器的全类名
4 \) N0 P# f9 y7 c6 e, X3 b对于本例而言,xwork-conversion.properties文件中的内容为:$ J; C, [1 i0 j8 T
java.util.Date=cn.itcast.conversion.DateConverter: Q, F9 s+ z0 v! T! E9 P8 e8 C
* l% _5 u; _" K# k, a& u23.输出带有占位符的国际化信息2 c" [; }! t3 L9 J
(1).资源文件中的内容如下:
) G# `7 ~6 T4 J8 `( fwelcom={0}欢迎来到传智播客{1}* e( ^$ i& [' q: S
在jsp页面中输出带占位符的国际化信息( \& w4 x' R! b, y7 Y+ p/ O+ G
<s:text name="welcom">
) x; |1 {. o( t2 W4 U1 L( z v- Y<s:param><s:propertyvalue="realname"/></s:param>
1 B; V2 }8 V3 K& D; g; h<s:param>学习</s:param>
# A" F: E" N9 y: J% g! P `/ z</s:text>
9 i' E# [3 o0 l) m8 x% X5 R9 m在Action类中获取带占位符的国际化信息,可以使用getText(String key,String[] args)或getText(StringaTextName,List args)方法.
: L/ o3 b4 P O* N6 s 0 \7 S; l5 B/ ^) M0 M
(2).占位符就当是一个变量参数,可以传递给定的参数值.
5 }2 s8 i/ g% d3 `' u( F8 y
K1 F7 {& S+ X" F24.为Action属性注入值
2 G; V" n3 Y! m3 |struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便的为Action中的属性注入值,注意:属性必须提供setter方法,; _0 X, U5 w: Y ?
public class HelloWorldAction{
+ F! g9 y9 J4 d# [ private String savePah;& f. t$ Z3 J: a( Z8 {
public String getSavePath(){
& U9 j6 n2 z. P+ {% I return savePath;' J. i+ m5 u4 A
}
0 a2 f. ^+ _2 p6 k3 w: M; G public void setSavePath(String savePath){
# v/ [5 U- G" [ this.savePath=savePath;3 W; T$ m3 h7 T. o/ Q3 }. o) U2 V( ^8 y
}) f+ T7 ?! X% L+ Y/ T/ N
}
1 N+ ~# ~, y8 o% A, a: ^<package name="itcast"namespace="/test" extends="struts-default">5 R# }0 F4 x, o4 D& b! }/ E
<action name="helloworld"class="cn.itcast.action.helloWorldAction">
0 W8 l) }# a8 o4 @: `7 T! u/ |<paramname="savePath">/images</param>
' E. F0 R: u3 r. |, p4 H% S' v: F<resultname="success">/WEB-INF/page/hello.jsp</result>
* }5 v0 Q6 @6 b9 b; K</action>7 [! ~9 z* h7 o& `7 h9 y* ?$ t' v z
</package> \, \8 K1 Q3 N' [. N
上面通过<param>节点为action的savePath属性注入"/images";Action的变量的值,不能写死,经常变换,需要通过配置来设置参数# k5 Z& m' k; w: |% ?
8 K( e) z- t$ H) y25.为应用指定多个配置文件$ d# u7 Q) I6 F! ]( d
(1).在大部分应用中,随着应用规模的增加,系统中的Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿,为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以讲一个struts.xml配置文件分解成多个配置文件,然后再struts.xml文件中包含其他配置文件,下面的struts.xml通过<include>元素指定多个配置文件:
% k @. R4 F6 b/ ^9 ]) ]: U/ Z<struts>* p! ~8 Y* i# X4 ~! S
<includefile="struts-user.xml"/>
, C' D: Y/ Q" v: m, h. b<includefile="struts-order.xml"/>7 b9 l# B1 s" P- g7 s0 S) s: j: L6 x9 ?
</struts>' O. y8 h' j* {- D9 m6 a
通过这种方式,我们就可以将struts2的Action按模块添加在多个配置文件中
3 Z' P5 y+ E8 I: h# j( R
' ?- q+ c7 i$ |; [26.文件上传2 I3 _& Z4 `( H; ~, |
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar,这两个文件可以从http://commons.apache.org下载,在struts2.1以前的版本需要添加,以后的版本就不需要添加
. T0 s# H2 F2 ^% n2 t
% |- W, e' ^3 N4 `7 V) {第二步:把form表的enctype设置为:"multipart/form.data",如下:; P* I9 H Q6 D' L
<form
+ h5 l X8 d4 p: Q5 r venctype="multipart/form-data"action="${pageContext.request.contextPath}/xxx.action"method="post">
7 t+ `% b7 ~6 M/ N<inputtype="file" name="uploadImage">这个属性的name必须要和类中File名称一样& s3 C0 g. o) ?) j0 n
</form>
; j) a0 k6 w) ~ {6 v2 o第三步:在Action类中添加以下属性) K5 ^2 M& ~( m6 `' i) D! K4 t/ n& O
public class HelloWorldAction{
; N: @. C; q' n3 C- ?6 }private File uploadImage;//得到上传文件;+ V$ ^/ ^- L! ^# L* u* R: Z
private String uploadImageContentType;//得到文件的类型
- C+ m& k# n, d7 }. Cprivate String uploadImageFileName;//得到文件的名称" V& \ |( ^. R4 Y8 w1 G5 e
//这里省略了属性的get/set方法(但是要注意get/set方法是必须的)0 x+ @ @7 j9 L2 U
public String upload() throws Exception{
$ Q3 V. U9 p3 [. s+ O% iString realpath =ServletActionContext.getServletContext().getRealPath("/images"); \* q% P- h& h
File file=new File(realpath);/ b. `! Z" R% j% _
if(file.getParentFile().exists())file.getParentFile().mkdirs();//目录是否存在,不存在就创建
# K: K, T# k% W u8 DFileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));8 p! Y% j- H4 ?$ L, I
return "success";" K+ R# t4 A! [5 O
}, X0 j4 v9 P* n9 |
}# I' {& n) u G6 e2 c0 w
8 ^9 }2 v3 {+ U; S% g(1).如果文件不保存,struts2会把文件保存到自己的目录中,但是当这个Action执行完后,该文件就会被删除,所以我们要将上传的文件保存到硬盘上; D0 A+ _0 d' F0 F$ t# q) W! z' a+ k
- `5 P5 c2 l/ l. l(2).最好还要判断以下,文件uploadImage是否为空
1 `( T3 @0 ] u1 `
" T+ F+ V9 O5 n$ n9 ] S7 _(3).如果上传大的文件,web都会失败,像一些门户网站上传视频,都是通过一个插件,可以把这个插件看成一个程序,只是这个程序是通过Socket变成的,针对一个端口进行传输数据
. K2 \ e/ q" ?3 S2 C8 q1 b 9 {7 S/ Z3 D: R: Y9 T8 j5 N
27.指定struts2处理的请求后缀
2 {6 O1 l; t: `0 m f(1).前面我们都是默认使用.action后缀访问Action,其实默认后缀是可以通过常量"struts.action.extension"进行修改的,例如:我们可以配置struts2只处理以.do为后缀的请求路径# e% G# x$ y+ a; ]& z
<struts>
' F9 t' k3 a) n/ L5 |# P s) p<constantname="struts.action.extendsion" value="do"/>/ c9 ?. Y4 b. c0 x- `; u
</struts>
, _' {/ q' @# e* [0 }如果用户需要制定多个请求后缀,则多个后缀之间以英文逗号","隔开,如:
1 I% S' t C1 C" u7 k, l3 b: F5 E<constantname="struts.action.extendsion" value="do,go"/>
& N: |( Z9 Y. v * p5 ^# n8 `' a v. l. }
(2).常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置,两种配置方式如下:
# G/ J# n2 L; q在struts.xml文件中配置常量:7 J6 H& c, G0 `" j
<struts>
/ F9 O _9 p* d% t+ O% A5 b<constantname="struts.action.extendsion" value="do"/>) D' l2 H* Y( C
</struts>+ \6 g& F' f) b7 c* c
在struts.properties中配置常量:
, I: F) C# v) v1 Cstruts.action.extension=do0 }) l( K( o# E- z: t5 c" C V! l5 z
因为常量可以在下面多个配置文件中进行定义,所以我们需要了解struts2加载常量的搜索顺序:; f8 A" _4 D7 p: q' E
struts-default.xml1 N% |# W! |' F3 H5 _/ R$ q
struts-plugin.xml W2 H, R6 i- \
struts.xml# \; J% y% v3 |& K6 [2 g
struts.properties
! W) p, G7 @( B, w& dweb.xml
3 H1 ^$ Y3 b( ~4 {如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值
& k7 K4 T* Z) V5 e
1 T! s2 p2 ^6 j- {6 E6 f(3).7 [+ s0 w) K( G9 t( x' H
第一:默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出:
( u- G; p; E( h, P# |<constantname="struts.i18n.encoding" value="UTF-8"/>
! h9 |2 q8 z1 y3 G, G第二:该属性指定需要struts2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由struts2处理,如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开
* b% @0 c. y( s& l _' f<constant name="strtus.action.extension" value=do"/> ^8 }1 g; @2 i
第三:设置浏览器是否缓存静态内容默认值为true(生产环境下使用)开发阶段最好关闭,不然看不到修改后的数据
- }. u( s: k% J6 v: u<constantname="struts.serve.static.browserCache" value="false"/>
8 c) [+ v' O) C/ f) G6 ^8 W第四:当struts的配置文件修改后系统是否自动重新加载该文件默认值为false(生产环境下使用),开发阶段最好打开- z6 j P: X" Q: r% S, ?/ \0 M
<constantname="struts.configuration.xml.reload" value="true"/>2 L' N/ C& P0 B2 v" h& z# g; E
第五:开发模式下使用,这样可以打印出更详细的错误信息+ r- S/ w* A- e) S/ f( V( @8 W ~
<constant name="struts.devMode"value="true"/>) G, r# V* {- z' D6 j! N
第六:默认的视图主题
8 H+ S3 |4 V' M7 u& }6 U<constantname="struts.ui.theme" value="simple"/>
( w# J/ _$ w8 v0 Y第七:与spring集成时,指定由spring负责action对象的创建8 E/ r- @; y) [, ]9 F5 M
<constantname="struts.objectFactory" value="spring"/>1 O" g' m/ ], G
第七:该属性设置struts2是否支持动态方法调用,该属性的默认值是true,如果需要关闭动态方法调用,则可设置该属性为false
$ {3 R6 M% t4 Y) j) e+ F<constantname="struts.enable.DynamicMethodInvocation"value="false"/>1 x( K t1 N- [( }( Q
第八:上传所有文件的总大小限制
# Z) F, q2 J: X+ Z% |" lconstantname="struts.mulitipart.maxSize" value="10701096"/>
3 L2 Q5 {1 D/ ^+ a3 b8 S1 L! E
' z* q2 H v+ q+ v& v- O28.自定义拦截器
& {0 Q g6 e: u" c(1).如果用户登录后可以访问action中的所有方法,如果用户没有登录不允许访问action中的方法,并且提示"你没有权限执行该操作"
5 b. n; g2 v' [- z O
2 e0 e& B2 z, W(2).
" h- ?2 u3 p/ d: B<interceptors>
( Q; w5 k1 v+ ^<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
2 r( X2 O5 E! b5 p6 P( e</interceptors>
2 K: o8 Y4 ~, c" A<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">' i& {2 b- M8 x) E
<interceptor-refname="permission"/>
1 G% w) e. B$ Q+ l3 X" j如果为某一个Action定义一个拦截器,struts2中对Action的默认的很多拦截器都失去功能,所以要想做到两全其美,需要定义一个拦截器栈:6 j9 W2 Y3 T: F0 P- G7 x
<interceptors>. ]& L0 o2 D! I+ X2 J/ j8 `9 J
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
3 S6 e* R2 S- Q3 d<interceptor-stackname="permissionStack">; Q$ @% ~$ T) K) f
<interceptor-refname="defaultStack"/>
/ _* m" V, x' f, V( G<interceptor-refname="permission"/>
- e! R* o9 O- H; |, n0 r</interceptor-stack>
7 K% M2 D$ y2 l1 g9 f2 t% W2 Y2 s. n</interceptors>
" \( h# H, s2 ~0 R: u# E$ s因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能,如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-refname="permissionStack"/>把拦截器定义为默认拦截器,注意:每个包只能指定一个默认拦截器,另外,一旦我们为该包中的某个action显示指定了某个拦截器,则默认拦截器不会起作用.$ L1 N/ x# t K9 g, x& o+ W8 c& T
(3).系统默认的拦截器可以到struts-default.xml中查看,很多功能.系统拦截器放在最前面,自定义的拦截器放在后面.
7 L+ [" `0 m7 H* N) }/ B$ V4 I
: }/ L+ \2 `& [+ ]29.自定义类型转换器
3 N6 ^% n- U d4 K5 x' r9 C(1).struts2中提供了两种类型转换器:局部类型转换器(只对某一个action起作用),全局类型转换器(所有的action起作用)
9 U: L) }: H% V9 K3 Y
% E/ t5 \% }) d2 t" ~(2).类型转换器必须继承DefaultTypeConverter最好用xwork2.jar中的,重写converValue(Map<String,Object>context,Objectvalue,Class toType){9 d7 p6 m" `6 N5 Q
returnsuper.convertValue(context,value,toType);' h o" B6 W8 ^
}, a& `4 o& v* f2 m( r8 \
其中第一个参数和ognl表达式,第二个参数是需要转换类型的内容(是String数组,因为可能有多个值),第三个参数是需要转换成什么类型,要实现双向转换
, c. d, _7 |+ r) C; x & v# b ~6 o" T J, I
(3).将上面的类型转换器注册为局部类型转换器:5 P% I/ |2 I7 b a2 [1 A$ h, _$ `5 V
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties.在properties文件中的内容为:
6 P; V) \( [/ |. {* l+ m需要转换的属性名称=类型转换器的全类名; c: y+ Y" w# q( ~9 u1 b& k
对于本例而言,HelloWorldAction-conversion.properties文件中的内容为:0 N, i4 L4 A6 b
createtime=cn.itcast.conversion.DateConverter |
|