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