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