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